Merge branch 'lyricnz-feature/locale-dates'

pull/209/head
Miguel Mota 3 years ago
commit 4ba9f52a87
No known key found for this signature in database
GPG Key ID: 67EC1161588A00F9

@ -291,7 +291,7 @@ func (ct *Cointop) GetCoinsTable() *table.Table {
})
case "last_updated":
unix, _ := strconv.ParseInt(coin.LastUpdated, 10, 64)
lastUpdated := time.Unix(unix, 0).Format("15:04:05 Jan 02")
lastUpdated := humanize.FormatTime(time.Unix(unix, 0), "15:04:05 Jan 02")
ct.SetTableColumnWidthFromString(header, lastUpdated)
ct.SetTableColumnAlignLeft(header, false)
rowCells = append(rowCells,

@ -290,7 +290,7 @@ func (ct *Cointop) GetPortfolioTable() *table.Table {
})
case "last_updated":
unix, _ := strconv.ParseInt(coin.LastUpdated, 10, 64)
lastUpdated := time.Unix(unix, 0).Format("15:04:05 Jan 02")
lastUpdated := humanize.FormatTime(time.Unix(unix, 0), "15:04:05 Jan 02")
ct.SetTableColumnWidthFromString(header, lastUpdated)
ct.SetTableColumnAlignLeft(header, false)
rowCells = append(rowCells,

@ -10,6 +10,8 @@ require (
github.com/fatih/color v1.13.0
github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1
github.com/gliderlabs/ssh v0.3.3
github.com/goodsign/monday v1.0.0
github.com/jeandeaual/go-locale v0.0.0-20210323163322-5cf4ff553a8d
github.com/maruel/panicparse v1.6.1
github.com/mattn/go-runewidth v0.0.13
github.com/miguelmota/go-coinmarketcap v0.1.8

@ -37,13 +37,16 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne v1.4.2/go.mod h1:xL4c3WmpE/Tvz5CEm5vqsaizU/EeOCm9DYlL2GtTSiM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/anaskhan96/soup v1.0.1 h1:3p9zOr7o2weHqDakRA1uR0SZNr6VhH5qPkm6p3gvS6o=
github.com/anaskhan96/soup v1.0.1/go.mod h1:pT5vs4HXDwA5y4KQCsKvnkpQd3D+joP7IqpiGskfWW0=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@ -84,6 +87,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fyne-io/mobile v0.1.2-0.20201127155338-06aeb98410cc/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1 h1:Xh9mvwEmhbdXlRSsgn+N0zj/NqnKvpeqL08oKDHln2s=
@ -91,15 +96,18 @@ github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1/go.mod h1:ElSskYZe
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -129,6 +137,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/goodsign/monday v1.0.0 h1:Yyk/s/WgudMbAJN6UWSU5xAs8jtNewfqtVblAlw0yoc=
github.com/goodsign/monday v1.0.0/go.mod h1:r4T4breXpoFwspQNM+u2sLxJb2zyTaxVGqUfTBjWOu8=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -192,6 +202,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/jeandeaual/go-locale v0.0.0-20210323163322-5cf4ff553a8d h1:VPBM6qn7uSBJroH61nL9yqtfK0xDWcmI5JHl11a2CJ0=
github.com/jeandeaual/go-locale v0.0.0-20210323163322-5cf4ff553a8d/go.mod h1:S0Hh9Ro1RbEQKfJA5XcnmGjcSQF+ZdSVikXVNQJMTAo=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@ -204,6 +218,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/maruel/panicparse v1.6.1 h1:803MjBzGcUgE1vYgg3UMNq3G1oyYeKkMu3t6hBS97x0=
github.com/maruel/panicparse v1.6.1/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0=
@ -243,6 +258,9 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.1.1/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@ -251,7 +269,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -273,14 +293,18 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -335,6 +359,7 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -449,12 +474,14 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -495,6 +522,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -516,6 +544,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -641,11 +670,13 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

@ -6,11 +6,16 @@ import (
"os"
"strconv"
"strings"
"time"
"github.com/goodsign/monday"
"github.com/jeandeaual/go-locale"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
var cachedSystemLocale = ""
// Numericf produces a string from of the given number with give fixed precision
// in base 10 with thousands separators after every three orders of magnitude
// using thousands and decimal separator according to LC_NUMERIC; defaulting "en".
@ -29,6 +34,61 @@ func Monetaryf(value float64, precision int) string {
return f(value, precision, "LC_MONETARY", false)
}
// borrowed from go-locale/util.go
func splitLocale(locale string) (string, string) {
// Remove the encoding, if present
formattedLocale := strings.Split(locale, ".")[0]
// Normalize by replacing the hyphens with underscores
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
// Split at the underscore
split := strings.Split(formattedLocale, "_")
language := split[0]
territory := ""
if len(split) > 1 {
territory = split[1]
}
return language, territory
}
// GetLocale returns the current locale as defined in IETF BCP 47 (e.g. "en-US").
// The envvar provided is checked first (eg LC_TIME), before the platform-specific defaults.
func getLocale(envvar string) string {
userLocale := "en-US" // default language-REGION
// First try looking up envar directly
envlang, ok := os.LookupEnv(envvar)
if ok {
language, region := splitLocale(envlang)
userLocale = language
if len(region) > 0 {
userLocale = strings.Join([]string{language, region}, "-")
}
} else {
// Then use (cached) system-specific locale
if cachedSystemLocale == "" {
if loc, err := locale.GetLocale(); err == nil {
userLocale = loc
cachedSystemLocale = loc
}
} else {
userLocale = cachedSystemLocale
}
}
return userLocale
}
// formatTimeExplicit formats the given time using the prescribed layout with the provided userLocale
func formatTimeExplicit(time time.Time, layout string, userLocale string) string {
mondayLocale := monday.Locale(strings.Replace(userLocale, "-", "_", 1))
return monday.Format(time, layout, mondayLocale)
}
// FormatTime is a dropin replacement time.Format(layout) that uses system locale + LC_TIME
func FormatTime(time time.Time, layout string) string {
return formatTimeExplicit(time, layout, getLocale("LC_TIME"))
}
// f formats given value, with precision decimal places using thousands and decimal
// separator according to language found in given locale environment variable e.
// If fixed is true the decimal places are fixed to the given precision otherwise d is the

@ -3,6 +3,7 @@ package humanize
import (
"fmt"
"testing"
"time"
)
// TestMonetary tests monetary formatting
@ -51,3 +52,43 @@ func TestScaleNumeric(t *testing.T) {
}
}
}
func TestFormatTime(t *testing.T) {
testData := map[string]map[string]string{
"en_GB": {
"Monday 2 January 2006": "Wednesday 12 March 2014",
"Jan 2006": "Mar 2014",
"02 Jan 2006": "12 Mar 2014",
"02/01/2006": "12/03/2014",
},
"en_US": {
"Monday 2 January 2006": "Wednesday 12 March 2014",
"Jan 2006": "Mar 2014",
"02 Jan 2006": "12 Mar 2014",
"02/01/2006": "12/03/2014", // ??
},
"fr_FR": {
"Monday 2 January 2006": "mercredi 12 mars 2014",
"Jan 2006": "mars 2014",
"02 Jan 2006": "12 mars 2014",
"02/01/2006": "12/03/2014",
},
"de_DE": {
"Monday 2 January 2006": "Mittwoch 12 März 2014",
"Jan 2006": "Mär 2014",
"02 Jan 2006": "12 Mär 2014",
"02/01/2006": "12/03/2014",
},
}
testTime := time.Date(2014, 3, 12, 0, 0, 0, 0, time.Local)
for locale, tests := range testData {
for layout, result := range tests {
s := formatTimeExplicit(testTime, layout, locale)
if s != result {
t.Fatalf("Expected layout '%s' in locale %s to render '%s' but got '%s'", layout, locale, result, s)
}
}
}
}

@ -4,6 +4,8 @@ import (
"math"
"sort"
"time"
"github.com/cointop-sh/cointop/pkg/humanize"
)
// ResampleTimeSeriesData resamples the given [timestamp,value] data to numsteps between start-end (returns numSteps+1 points).
@ -59,7 +61,8 @@ func BuildTimeSeriesLabels(data [][]float64) []string {
}
var labels []string
for i := range data {
labels = append(labels, time.UnixMilli(int64(data[i][0])).Format(timeFormat))
labelTime := time.UnixMilli(int64(data[i][0]))
labels = append(labels, humanize.FormatTime(labelTime, timeFormat))
}
return labels
}

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
/monday.iml
/.idea/

@ -0,0 +1,9 @@
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,161 @@
Description
====
Monday is a minimalistic translator for month and day of week names in time.Date objects. Supports 20+ different locales.
Written in pure [Go](http://golang.org).
![Go](https://github.com/goodsign/monday/workflows/Go/badge.svg)
Installing
====
```
go get github.com/goodsign/monday
```
Usage
====
Format
---------------------
Given that you already use [time.Format](http://golang.org/pkg/time/#Time.Format) somewhere in your code,
to translate your output you should import monday and replace
```go
yourTime.Format(yourLayout)
```
with
```go
// Change LocaleEnUS to the locale you want to use for translation
monday.Format(yourTime, yourLayout, monday.LocaleEnUS)
```
Parse
---------------------
Given that you already use [time.ParseInLocation](http://golang.org/pkg/time/#ParseInLocation) somewhere in your code,
to parse input string in a different language you should import monday and replace
```go
time.ParseInLocation(yourLayout, yourString, yourLocation)
```
with
```go
// Change LocaleEnUS to the locale you want to use for translation
monday.ParseInLocation(yourLayout, yourString, yourLocation, monday.LocaleEnUS)
```
Predefined formats
---------------------
Monday declares some predefined formats: Full, Long, Medium, Short, DateTime formats for each locale. E.g. to get
short format for any locale you can use map:
```go
monday.ShortFormatsByLocale[locale]
```
Usage notes
-----------
**Monday** is not an alternative to standard **time** package. It is a temporary solution to use while
the internationalization features are not ready.
That's why **monday** doesn't create any additional parsing algorithms, layout identifiers. It is just
a wrapper for time.Format and time.ParseInLocation and uses all the same layout IDs, constants, etc.
So, the changes you need to temporarily switch to **monday** (while the internationalization features are being developed)
are minimal: you preserve your layout, your time object, your parsed date string formats and the only change is
the func call itself.
Locales
----
Supported locales are listed in **locale.go** file.
```
const (
LocaleEnUS = "en_US" // English (United States)
LocaleEnGB = "en_GB" // English (United Kingdom)
LocaleDaDK = "da_DK" // Danish (Denmark)
LocaleNlBE = "nl_BE" // Dutch (Belgium)
LocaleNlNL = "nl_NL" // Dutch (Netherlands)
LocaleFiFI = "fi_FI" // Finnish (Finland)
LocaleFrFR = "fr_FR" // French (France)
LocaleFrCA = "fr_CA" // French (Canada)
LocaleDeDE = "de_DE" // German (Germany)
LocaleHuHU = "hu_HU" // Hungarian (Hungary)
LocaleItIT = "it_IT" // Italian (Italy)
LocaleNnNO = "nn_NO" // Norwegian Nynorsk (Norway)
LocaleNbNO = "nb_NO" // Norwegian Bokmål (Norway)
LocalePlPL = "pl_PL" // Polish (Poland)
LocalePtPT = "pt_PT" // Portuguese (Portugal)
LocalePtBR = "pt_BR" // Portuguese (Brazil)
LocaleRoRO = "ro_RO" // Romanian (Romania)
LocaleRuRU = "ru_RU" // Russian (Russia)
LocaleEsES = "es_ES" // Spanish (Spain)
LocaleCaES = "ca_ES" // Catalan (Spain)
LocaleSvSE = "sv_SE" // Swedish (Sweden)
LocaleTrTR = "tr_TR" // Turkish (Turkey)
LocaleUkUA = "uk_UA" // Ukrainian (Ukraine)
LocaleBgBG = "bg_BG" // Bulgarian (Bulgaria)
LocaleZhCN = "zh_CN" // Chinese (Mainland)
LocaleZhTW = "zh_TW" // Chinese (Taiwan)
LocaleZhHK = "zh_HK" // Chinese (Hong Kong)
LocaleKoKR = "ko_KR" // Korean (Korea)
LocaleJaJP = "ja_JP" // Japanese (Japan)
LocaleElGR = "el_GR" // Greek (Greece)
LocaleIdID = "id_ID" // Indonesian (Indonesia)
LocaleFrGP = "fr_GP" // French (Guadeloupe)
LocaleFrLU = "fr_LU" // French (Luxembourg)
LocaleFrMQ = "fr_MQ" // French (Martinique)
LocaleFrGF = "fr_GF" // French (French Guiana)
LocaleFrGF = "fr_RE" // French (Reunion)
LocaleCsCZ = "cs_CZ" // Czech (Czech Republic)
LocaleSlSI = "sl_SI" // Slovenian (Slovenia)
)
```
LocaleDetector
====
```go
var timeLocaleDetector *monday.LocaleDetector = monday.NewLocaleDetector()
dateTime, err := timeLocaleDetector.Parse(layout,datestr)
```
parses datetime with **unknown** locale (for now - layout must be defined, as for time.Parse())
useful for text parsing tools/crawlers (f.e.: rss-feeds crawler)
TODO:
* make LocaleDetector insensitive to whitespaces count
Thread-safety
====
**Monday** initializes all its data once in the **init** func and then uses only
func calls and local vars. Thus, it's thread-safe and doesn't need any mutexes to be
used with.
Monday Licence
==========
The **Monday** library is released under the [BSD Licence](http://opensource.org/licenses/bsd-license.php)
[LICENCE file](https://github.com/goodsign/monday/blob/master/LICENCE)
Thanks
==========
* [Martin Angers](https://github.com/PuerkitoBio)
* Andrey Mirtchovski
* [mikespook](https://github.com/mikespook)
* [Luis Azevedo](https://github.com/braceta)
* [imikod](https://github.com/imikod)
* [Renato Serra](https://github.com/RenatoSerra22)
* [Zachary Stewart](https://github.com/ztstewart)

@ -0,0 +1,345 @@
package monday
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"text/scanner"
"time"
)
var (
wordsRx = regexp.MustCompile("(\\p{L}+)")
debugLayoutDef = false
)
// An InvalidTypeError indicates that data was parsed incorrectly as a result
// of a type mismatch.
type InvalidTypeError struct {
error
}
// An InvalidLengthError is returned when an item's length was longer or
// shorter than expected for a particular token.
type InvalidLengthError struct {
error
}
// NewInvalidTypeError instantiates an InvalidTypeError.
func NewInvalidTypeError() InvalidTypeError {
return InvalidTypeError{error: errors.New("invalid type for token")}
}
// NewInvalidLengthError instantiates an InvalidLengthError.
func NewInvalidLengthError() InvalidLengthError {
return InvalidLengthError{error: errors.New("invalid length for token")}
}
type layoutSpanI interface {
scanInt(s *scanner.Scanner) (int, error)
scanString(s *scanner.Scanner) (string, error)
isString() bool
isDelimiter() bool
}
type lengthLimitSpan struct {
minLength int
maxLength int
}
func (lls lengthLimitSpan) scanInt(s *scanner.Scanner) (int, error) {
return -1, NewInvalidTypeError()
}
func (lls lengthLimitSpan) scanString(s *scanner.Scanner) (string, error) {
return "", NewInvalidTypeError()
}
func (lls lengthLimitSpan) isString() bool { return false }
func (lls lengthLimitSpan) isDelimiter() bool { return false }
func initLengthLimitSpan(min, max int) lengthLimitSpan {
return lengthLimitSpan{
minLength: min,
maxLength: max,
}
}
type limitedStringSpan struct {
lengthLimitSpan
}
func initLimitedStringSpan(minLength, maxLength int) limitedStringSpan {
return limitedStringSpan{lengthLimitSpan: initLengthLimitSpan(minLength, maxLength)}
}
func (lss limitedStringSpan) scanString(s *scanner.Scanner) (string, error) {
tok := s.Scan()
if tok != scanner.EOF && tok == -2 {
return s.TokenText(), nil
}
return "", NewInvalidTypeError()
}
func (lss limitedStringSpan) isString() bool { return true }
func (lss limitedStringSpan) String() string {
return fmt.Sprintf("[limitedStringSpan:%v]", lss.lengthLimitSpan)
}
type rangeIntSpan struct {
lengthLimitSpan
min int
max int
}
func initRangeIntSpan(minValue, maxValue, minLength, maxLength int) rangeIntSpan {
return rangeIntSpan{
lengthLimitSpan: initLengthLimitSpan(minLength, maxLength),
min: minValue,
max: maxValue,
}
}
func (rs rangeIntSpan) scanInt(s *scanner.Scanner) (int, error) {
var tok = s.Scan()
var negative bool
if tok == 45 {
negative = true
if debugLayoutDef {
fmt.Printf("scan negative:'%s'\n", s.TokenText())
}
tok = s.Scan()
} else if tok == 43 { // positive
tok = s.Scan()
}
if tok == -3 {
str := s.TokenText()
i, err := strconv.Atoi(str)
if err != nil {
return 0, err
}
if negative {
i = i * -1
}
return i, nil
}
if debugLayoutDef {
fmt.Printf("invalid tok: %v '%s'\n", tok, s.TokenText())
}
return 0, NewInvalidTypeError()
}
func (rs rangeIntSpan) String() string {
return fmt.Sprintf("[rangeIntSpan:%v]", rs.lengthLimitSpan)
}
type delimiterSpan struct {
lengthLimitSpan
character string
}
func initDelimiterSpan(character string, minLength, maxLength int) delimiterSpan {
return delimiterSpan{
lengthLimitSpan: initLengthLimitSpan(minLength, maxLength),
character: character,
}
}
func (ds delimiterSpan) scanString(s *scanner.Scanner) (string, error) {
tok := s.Scan()
if tok != scanner.EOF && tok != -2 && tok != 45 && tok != -3 {
return s.TokenText(), nil
}
if debugLayoutDef {
fmt.Printf("expected tok:=!(-2,-3,45), received:%d ('%s')\n", tok, s.TokenText())
}
return "", NewInvalidTypeError()
}
func (ds delimiterSpan) isString() bool { return false }
func (ds delimiterSpan) isDelimiter() bool { return true }
func (ds delimiterSpan) String() string {
return fmt.Sprintf("[delimiterSpan '%s':%v]", ds.character, ds.lengthLimitSpan)
}
type layoutDef struct {
spans []layoutSpanI
errorPosition int
}
func (ld *layoutDef) validate(value string) bool {
s := &scanner.Scanner{}
s.Init(strings.NewReader(value))
s.Whitespace = 0
for _, span := range ld.spans {
if span.isString() || span.isDelimiter() {
if _, err := span.scanString(s); err != nil {
ld.errorPosition = s.Pos().Offset
if debugLayoutDef {
fmt.Printf("error at pos: %d: %s (span=%+v) - expected string or delimiter\n", s.Pos().Offset, err.Error(), span)
}
return false
}
} else if _, err := span.scanInt(s); err != nil {
if debugLayoutDef {
fmt.Printf("error at pos: %d: %s (span=%+v) - expected integer\n", s.Pos().Offset, err.Error(), span)
}
ld.errorPosition = s.Pos().Offset
return false
}
}
ld.errorPosition = s.Pos().Offset
return s.Pos().Offset == len(value)
}
// A LocaleDetector parses time.Time values by using various heuristics and
// techniques to determine which locale should be used to parse the
// time.Time value. As not all possible locales and formats are supported,
// this process can be somewhat lossy and inaccurate.
type LocaleDetector struct {
localeMap map[string]*set
lastLocale Locale
layoutsMap map[string]layoutDef
lastErrorPosition int
}
func (ld *LocaleDetector) prepareLayout(layout string) layoutDef {
s := scanner.Scanner{}
s.Init(strings.NewReader(layout))
s.Whitespace = 0
result := make([]layoutSpanI, 0)
var tok rune
// var pos int = 0
var span layoutSpanI
var sign bool
// var neg bool = false
for tok != scanner.EOF {
tok = s.Scan()
switch tok {
case -2: // text
span = initLimitedStringSpan(1, -1)
case -3: // digit
span = initRangeIntSpan(-1, -1, 1, -1)
if sign {
sign = false
}
case 45: // negative sign
sign = true
// neg = s.TokenText() == "-"
continue
case 43: // positive sign
sign = true
continue
case scanner.EOF:
continue
default: // fixed character
span = initDelimiterSpan(s.TokenText(), 1, 1)
}
result = append(result, span)
// length := s.Pos().Offset - pos
// pos = s.Pos().Offset
// fmt.Printf("tok'%s' [%d %d] length=%d\n", s.TokenText(), pos, s.Pos().Offset, length)
}
if debugLayoutDef {
fmt.Printf("layout:'%s'\n", layout)
fmt.Printf("layout:%v\n", result)
}
ret := layoutDef{spans: result}
ld.layoutsMap[layout] = ret
return ret
}
func (ld *LocaleDetector) validateValue(layout string, value string) bool {
l, ok := ld.layoutsMap[layout]
if !ok {
l = ld.prepareLayout(layout)
}
result := l.validate(value)
ld.lastErrorPosition = l.errorPosition
return result
}
func (ld *LocaleDetector) errorPosition() int { return ld.lastErrorPosition }
func (ld *LocaleDetector) addWords(words []string, v Locale) {
for _, w := range words {
l := strings.ToLower(w)
if _, ok := ld.localeMap[w]; !ok {
ld.localeMap[w] = newSet(v)
if l != w {
ld.localeMap[l] = newSet(v)
}
} else {
ld.localeMap[w].Add(v)
if l != w {
ld.localeMap[l].Add(v)
}
}
}
}
// NewLocaleDetector instances a LocaleDetector instance.
func NewLocaleDetector() *LocaleDetector {
ld := &LocaleDetector{localeMap: make(map[string]*set), lastLocale: LocaleEnGB, layoutsMap: make(map[string]layoutDef)}
for _, v := range ListLocales() {
days := GetShortDays(v)
ld.addWords(days, v)
days = GetLongDays(v)
ld.addWords(days, v)
months := GetShortMonths(v)
ld.addWords(months, v)
months = GetLongMonths(v)
ld.addWords(months, v)
}
return ld
}
// Parse will attempt to parse a time.Time struct from a layout (format) and a
// value to parse from.
//
// If no locale can be determined, this method will return an error and an
// empty time object.
func (ld *LocaleDetector) Parse(layout, value string) (time.Time, error) {
if ld.validateValue(layout, value) {
ld.lastLocale = ld.detectLocale(value)
return ParseInLocation(layout, value, time.UTC, ld.lastLocale)
}
return time.Time{}, &time.ParseError{
Value: value,
Layout: layout,
Message: fmt.Sprintf("'%s' not matches to '%s' last error position = %d\n", value, layout, ld.lastErrorPosition),
}
}
func (ld *LocaleDetector) detectLocale(value string) Locale {
localesMap := make(map[Locale]int)
for _, v := range wordsRx.FindAllStringSubmatchIndex(value, -1) {
word := strings.ToLower(value[v[0]:v[1]])
if localesSet, ok := ld.localeMap[word]; ok {
localesSet.Each(func(loc Locale) bool {
if _, ok := localesMap[loc]; !ok {
localesMap[loc] = 1
} else {
localesMap[loc]++
}
return true
})
}
}
var result Locale = LocaleEnUS
frequency := 0
for key, counter := range localesMap {
if counter > frequency {
frequency = counter
result = key
}
}
return result
}

@ -0,0 +1,412 @@
package monday
// Default date formats by country.
// Mostly taken from http://en.wikipedia.org/wiki/Date_format_by_country
const (
DefaultFormatEnUS = "01/02/06"
DefaultFormatEnUSFull = "Monday, January 2, 2006" // English (United States)
DefaultFormatEnUSLong = "January 2, 2006"
DefaultFormatEnUSMedium = "Jan 02, 2006"
DefaultFormatEnUSShort = "1/2/06"
DefaultFormatEnUSDateTime = "1/2/06 3:04 PM"
DefaultFormatEnGBFull = "Monday, 2 January 2006" // English (United Kingdom)
DefaultFormatEnGBLong = "2 January 2006"
DefaultFormatEnGBMedium = "02 Jan 2006"
DefaultFormatEnGBShort = "02/01/2006"
DefaultFormatEnGBDateTime = "02/01/2006 15:04"
DefaultFormatDaDKFull = "Monday den 2. January 2006" // Danish (Denmark)
DefaultFormatDaDKLong = "2. Jan 2006"
DefaultFormatDaDKMedium = "02/01/2006"
DefaultFormatDaDKShort = "02/01/06"
DefaultFormatDaDKDateTime = "02/01/2006 15.04"
DefaultFormatNlBEFull = "Monday 2 January 2006" // Dutch (Belgium)
DefaultFormatNlBELong = "2 January 2006"
DefaultFormatNlBEMedium = "02-Jan-2006"
DefaultFormatNlBEShort = "2/01/06"
DefaultFormatNlBEDateTime = "2/01/06 15:04"
DefaultFormatNlNLFull = "Monday 2 January 2006" // Dutch (Netherlands)
DefaultFormatNlNLLong = "2 January 2006"
DefaultFormatNlNLMedium = "02 Jan 2006"
DefaultFormatNlNLShort = "02-01-06"
DefaultFormatNlNLDateTime = "02-01-06 15:04"
DefaultFormatFiFIFull = "Monday 2. January 2006" // Finnish (Finland)
DefaultFormatFiFILong = "2. January 2006"
DefaultFormatFiFIMedium = "02.1.2006"
DefaultFormatFiFIShort = "02.1.2006"
DefaultFormatFiFIDateTime = "02.1.2006 15.04"
DefaultFormatFrFRFull = "Monday 2 January 2006" // French (France)
DefaultFormatFrFRLong = "2 January 2006"
DefaultFormatFrFRMedium = "02 Jan 2006"
DefaultFormatFrFRShort = "02/01/2006"
DefaultFormatFrFRDateTime = "02/01/2006 15:04"
DefaultFormatFrCAFull = "Monday 2 January 2006" // French (Canada)
DefaultFormatFrCALong = "2 January 2006"
DefaultFormatFrCAMedium = "2006-01-02"
DefaultFormatFrCAShort = "06-01-02"
DefaultFormatFrCADateTime = "06-01-02 15:04"
DefaultFormatFrGPFull = "Monday 2 January 2006" // French (Canada)
DefaultFormatFrGPLong = "2 January 2006"
DefaultFormatFrGPMedium = "2006-01-02"
DefaultFormatFrGPShort = "06-01-02"
DefaultFormatFrGPDateTime = "06-01-02 15:04"
DefaultFormatFrLUFull = "Monday 2 January 2006" // French (Luxembourg)
DefaultFormatFrLULong = "2 January 2006"
DefaultFormatFrLUMedium = "2006-01-02"
DefaultFormatFrLUShort = "06-01-02"
DefaultFormatFrLUDateTime = "06-01-02 15:04"
DefaultFormatFrMQFull = "Monday 2 January 2006" // French (Martinique)
DefaultFormatFrMQLong = "2 January 2006"
DefaultFormatFrMQMedium = "2006-01-02"
DefaultFormatFrMQShort = "06-01-02"
DefaultFormatFrMQDateTime = "06-01-02 15:04"
DefaultFormatFrGFFull = "Monday 2 January 2006" // French (French Guiana)
DefaultFormatFrGFLong = "2 January 2006"
DefaultFormatFrGFMedium = "2006-01-02"
DefaultFormatFrGFShort = "06-01-02"
DefaultFormatFrGFDateTime = "06-01-02 15:04"
DefaultFormatFrREFull = "Monday 2 January 2006" // French (Reunion)
DefaultFormatFrRELong = "2 January 2006"
DefaultFormatFrREMedium = "2006-01-02"
DefaultFormatFrREShort = "06-01-02"
DefaultFormatFrREDateTime = "06-01-02 15:04"
DefaultFormatDeDEFull = "Monday, 2. January 2006" // German (Germany)
DefaultFormatDeDELong = "2. January 2006"
DefaultFormatDeDEMedium = "02.01.2006"
DefaultFormatDeDEShort = "02.01.06"
DefaultFormatDeDEDateTime = "02.01.06 15:04"
DefaultFormatHuHUFull = "2006. January 2., Monday" // Hungarian (Hungary)
DefaultFormatHuHULong = "2006. January 2."
DefaultFormatHuHUMedium = "2006.01.02."
DefaultFormatHuHUShort = "2006.01.02."
DefaultFormatHuHUDateTime = "2006.01.02. 15:04"
DefaultFormatItITFull = "Monday 2 January 2006" // Italian (Italy)
DefaultFormatItITLong = "2 January 2006"
DefaultFormatItITMedium = "02/Jan/2006"
DefaultFormatItITShort = "02/01/06"
DefaultFormatItITDateTime = "02/01/06 15:04"
DefaultFormatNnNOFull = "Monday 2. January 2006" // Norwegian Nynorsk (Norway)
DefaultFormatNnNOLong = "2. January 2006"
DefaultFormatNnNOMedium = "02. Jan 2006"
DefaultFormatNnNOShort = "02.01.06"
DefaultFormatNnNODateTime = "02.01.06 15:04"
DefaultFormatNbNOFull = "Monday 2. January 2006" // Norwegian Bokmål (Norway)
DefaultFormatNbNOLong = "2. January 2006"
DefaultFormatNbNOMedium = "02. Jan 2006"
DefaultFormatNbNOShort = "02.01.06"
DefaultFormatNbNODateTime = "15:04 02.01.06"
DefaultFormatPtPTFull = "Monday, 2 de January de 2006" // Portuguese (Portugal)
DefaultFormatPtPTLong = "2 de January de 2006"
DefaultFormatPtPTMedium = "02/01/2006"
DefaultFormatPtPTShort = "02/01/06"
DefaultFormatPtPTDateTime = "02/01/06, 15:04"
DefaultFormatPtBRFull = "Monday, 2 de January de 2006" // Portuguese (Brazil)
DefaultFormatPtBRLong = "02 de January de 2006"
DefaultFormatPtBRMedium = "02/01/2006"
DefaultFormatPtBRShort = "02/01/06"
DefaultFormatPtBRDateTime = "02/01/06, 15:04"
DefaultFormatRoROFull = "Monday, 02 January 2006" // Romanian (Romania)
DefaultFormatRoROLong = "02 January 2006"
DefaultFormatRoROMedium = "02.01.2006"
DefaultFormatRoROShort = "02.01.2006"
DefaultFormatRoRODateTime = "02.01.06, 15:04"
DefaultFormatRuRUFull = "Monday, 2 January 2006 г." // Russian (Russia)
DefaultFormatRuRULong = "2 January 2006 г."
DefaultFormatRuRUMedium = "02 Jan 2006 г."
DefaultFormatRuRUShort = "02.01.06"
DefaultFormatRuRUDateTime = "02.01.06, 15:04"
DefaultFormatEsESFull = "Monday, 2 de January de 2006" // Spanish (Spain)
DefaultFormatEsESLong = "2 de January de 2006"
DefaultFormatEsESMedium = "02/01/2006"
DefaultFormatEsESShort = "02/01/06"
DefaultFormatEsESDateTime = "02/01/06 15:04"
DefaultFormatCaESFull = "Monday, 2 de January de 2006" // Spanish (Spain)
DefaultFormatCaESLong = "2 de January de 2006"
DefaultFormatCaESMedium = "02/01/2006"
DefaultFormatCaESShort = "02/01/06"
DefaultFormatCaESDateTime = "02/01/06 15:04"
DefaultFormatSvSEFull = "Mondayen den 2:e January 2006" // Swedish (Sweden)
DefaultFormatSvSELong = "2 January 2006"
DefaultFormatSvSEMedium = "2 Jan 2006"
DefaultFormatSvSEShort = "2006-01-02"
DefaultFormatSvSEDateTime = "2006-01-02 15:04"
DefaultFormatTrTRFull = "2 January 2006 Monday" // Turkish (Turkey)
DefaultFormatTrTRLong = "2 January 2006"
DefaultFormatTrTRMedium = "2 Jan 2006"
DefaultFormatTrTRShort = "2.01.2006"
DefaultFormatTrTRDateTime = "2.01.2006 15:04"
DefaultFormatUkUAFull = "Monday, 2 January 2006 р." // Ukrainian (Ukraine)
DefaultFormatUkUALong = "2 January 2006 р."
DefaultFormatUkUAMedium = "02 Jan 2006 р."
DefaultFormatUkUAShort = "02.01.06"
DefaultFormatUkUADateTime = "02.01.06, 15:04"
DefaultFormatBgBGFull = "Monday, 2 January 2006" // Bulgarian (Bulgaria)
DefaultFormatBgBGLong = "2 January 2006"
DefaultFormatBgBGMedium = "2 Jan 2006"
DefaultFormatBgBGShort = "2.01.2006"
DefaultFormatBgBGDateTime = "2.01.2006 15:04"
DefaultFormatZhCNFull = "2006年1月2日 Monday" // Chinese (Mainland)
DefaultFormatZhCNLong = "2006年1月2日"
DefaultFormatZhCNMedium = "2006-01-02"
DefaultFormatZhCNShort = "2006/1/2"
DefaultFormatZhCNDateTime = "2006-01-02 15:04"
DefaultFormatZhTWFull = "2006年1月2日 Monday" // Chinese (Taiwan)
DefaultFormatZhTWLong = "2006年1月2日"
DefaultFormatZhTWMedium = "2006-01-02"
DefaultFormatZhTWShort = "2006/1/2"
DefaultFormatZhTWDateTime = "2006-01-02 15:04"
DefaultFormatZhHKFull = "2006年1月2日 Monday" // Chinese (Hong Kong)
DefaultFormatZhHKLong = "2006年1月2日"
DefaultFormatZhHKMedium = "2006-01-02"
DefaultFormatZhHKShort = "2006/1/2"
DefaultFormatZhHKDateTime = "2006-01-02 15:04"
DefaultFormatKoKRFull = "2006년1월2일 월요일" // Korean (Korea)
DefaultFormatKoKRLong = "2006년1월2일"
DefaultFormatKoKRMedium = "2006-01-02"
DefaultFormatKoKRShort = "2006/1/2"
DefaultFormatKoKRDateTime = "2006-01-02 15:04"
DefaultFormatJaJPFull = "2006年1月2日 Monday" // Japanese (Japan)
DefaultFormatJaJPLong = "2006年1月2日"
DefaultFormatJaJPMedium = "2006/01/02"
DefaultFormatJaJPShort = "2006/1/2"
DefaultFormatJaJPDateTime = "2006/01/02 15:04"
DefaultFormatElGRFull = "Monday, 2 January 2006" // Greek (Greece)
DefaultFormatElGRLong = "2 January 2006"
DefaultFormatElGRMedium = "2 Jan 2006"
DefaultFormatElGRShort = "02/01/06"
DefaultFormatElGRDateTime = "02/01/06 15:04"
DefaultFormatCsCZFull = "Monday, 2. January 2006" // Czech (Czech Republic)
DefaultFormatCsCZLong = "2. January 2006"
DefaultFormatCsCZMedium = "02 Jan 2006"
DefaultFormatCsCZShort = "02/01/2006"
DefaultFormatCsCZDateTime = "02/01/2006 15:04"
)
// FullFormatsByLocale maps locales to the'full' date formats for all
// supported locales.
var FullFormatsByLocale = map[Locale]string{
LocaleEnUS: DefaultFormatEnUSFull,
LocaleEnGB: DefaultFormatEnGBFull,
LocaleDaDK: DefaultFormatDaDKFull,
LocaleNlBE: DefaultFormatNlBEFull,
LocaleNlNL: DefaultFormatNlNLFull,
LocaleFiFI: DefaultFormatFiFIFull,
LocaleFrFR: DefaultFormatFrFRFull,
LocaleFrCA: DefaultFormatFrCAFull,
LocaleFrGP: DefaultFormatFrGPFull,
LocaleFrLU: DefaultFormatFrLUFull,
LocaleFrMQ: DefaultFormatFrMQFull,
LocaleFrGF: DefaultFormatFrGFFull,
LocaleFrRE: DefaultFormatFrREFull,
LocaleDeDE: DefaultFormatDeDEFull,
LocaleHuHU: DefaultFormatHuHUFull,
LocaleItIT: DefaultFormatItITFull,
LocaleNnNO: DefaultFormatNnNOFull,
LocaleNbNO: DefaultFormatNbNOFull,
LocalePtPT: DefaultFormatPtPTFull,
LocalePtBR: DefaultFormatPtBRFull,
LocaleRoRO: DefaultFormatRoROFull,
LocaleRuRU: DefaultFormatRuRUFull,
LocaleEsES: DefaultFormatEsESFull,
LocaleCaES: DefaultFormatCaESFull,
LocaleSvSE: DefaultFormatSvSEFull,
LocaleTrTR: DefaultFormatTrTRFull,
LocaleBgBG: DefaultFormatBgBGFull,
LocaleZhCN: DefaultFormatZhCNFull,
LocaleZhTW: DefaultFormatZhTWFull,
LocaleZhHK: DefaultFormatZhHKFull,
LocaleKoKR: DefaultFormatKoKRFull,
LocaleJaJP: DefaultFormatJaJPFull,
LocaleElGR: DefaultFormatElGRFull,
LocaleCsCZ: DefaultFormatCsCZFull,
}
// LongFormatsByLocale maps locales to the 'long' date formats for all
// supported locales.
var LongFormatsByLocale = map[Locale]string{
LocaleEnUS: DefaultFormatEnUSLong,
LocaleEnGB: DefaultFormatEnGBLong,
LocaleDaDK: DefaultFormatDaDKLong,
LocaleNlBE: DefaultFormatNlBELong,
LocaleNlNL: DefaultFormatNlNLLong,
LocaleFiFI: DefaultFormatFiFILong,
LocaleFrFR: DefaultFormatFrFRLong,
LocaleFrCA: DefaultFormatFrCALong,
LocaleFrGP: DefaultFormatFrGPLong,
LocaleFrLU: DefaultFormatFrLULong,
LocaleFrMQ: DefaultFormatFrMQLong,
LocaleFrRE: DefaultFormatFrRELong,
LocaleFrGF: DefaultFormatFrGFLong,
LocaleDeDE: DefaultFormatDeDELong,
LocaleHuHU: DefaultFormatHuHULong,
LocaleItIT: DefaultFormatItITLong,
LocaleNnNO: DefaultFormatNnNOLong,
LocaleNbNO: DefaultFormatNbNOLong,
LocalePtPT: DefaultFormatPtPTLong,
LocalePtBR: DefaultFormatPtBRLong,
LocaleRoRO: DefaultFormatRoROLong,
LocaleRuRU: DefaultFormatRuRULong,
LocaleEsES: DefaultFormatEsESLong,
LocaleCaES: DefaultFormatCaESLong,
LocaleSvSE: DefaultFormatSvSELong,
LocaleTrTR: DefaultFormatTrTRLong,
LocaleBgBG: DefaultFormatBgBGLong,
LocaleZhCN: DefaultFormatZhCNLong,
LocaleZhTW: DefaultFormatZhTWLong,
LocaleZhHK: DefaultFormatZhHKLong,
LocaleKoKR: DefaultFormatKoKRLong,
LocaleJaJP: DefaultFormatJaJPLong,
LocaleElGR: DefaultFormatElGRLong,
LocaleCsCZ: DefaultFormatCsCZLong,
}
// MediumFormatsByLocale maps locales to the 'medium' date formats for all
// supported locales.
var MediumFormatsByLocale = map[Locale]string{
LocaleEnUS: DefaultFormatEnUSMedium,
LocaleEnGB: DefaultFormatEnGBMedium,
LocaleDaDK: DefaultFormatDaDKMedium,
LocaleNlBE: DefaultFormatNlBEMedium,
LocaleNlNL: DefaultFormatNlNLMedium,
LocaleFiFI: DefaultFormatFiFIMedium,
LocaleFrFR: DefaultFormatFrFRMedium,
LocaleFrGP: DefaultFormatFrGPMedium,
LocaleFrCA: DefaultFormatFrCAMedium,
LocaleFrLU: DefaultFormatFrLUMedium,
LocaleFrMQ: DefaultFormatFrMQMedium,
LocaleFrGF: DefaultFormatFrGFMedium,
LocaleFrRE: DefaultFormatFrREMedium,
LocaleDeDE: DefaultFormatDeDEMedium,
LocaleHuHU: DefaultFormatHuHUMedium,
LocaleItIT: DefaultFormatItITMedium,
LocaleNnNO: DefaultFormatNnNOMedium,
LocaleNbNO: DefaultFormatNbNOMedium,
LocalePtPT: DefaultFormatPtPTMedium,
LocalePtBR: DefaultFormatPtBRMedium,
LocaleRoRO: DefaultFormatRoROMedium,
LocaleRuRU: DefaultFormatRuRUMedium,
LocaleEsES: DefaultFormatEsESMedium,
LocaleCaES: DefaultFormatCaESMedium,
LocaleSvSE: DefaultFormatSvSEMedium,
LocaleTrTR: DefaultFormatTrTRMedium,
LocaleBgBG: DefaultFormatBgBGMedium,
LocaleZhCN: DefaultFormatZhCNMedium,
LocaleZhTW: DefaultFormatZhTWMedium,
LocaleZhHK: DefaultFormatZhHKMedium,
LocaleKoKR: DefaultFormatKoKRMedium,
LocaleJaJP: DefaultFormatJaJPMedium,
LocaleElGR: DefaultFormatElGRMedium,
LocaleCsCZ: DefaultFormatCsCZMedium,
}
// ShortFormatsByLocale maps locales to the 'short' date formats for all
// supported locales.
var ShortFormatsByLocale = map[Locale]string{
LocaleEnUS: DefaultFormatEnUSShort,
LocaleEnGB: DefaultFormatEnGBShort,
LocaleDaDK: DefaultFormatDaDKShort,
LocaleNlBE: DefaultFormatNlBEShort,
LocaleNlNL: DefaultFormatNlNLShort,
LocaleFiFI: DefaultFormatFiFIShort,
LocaleFrFR: DefaultFormatFrFRShort,
LocaleFrCA: DefaultFormatFrCAShort,
LocaleFrLU: DefaultFormatFrLUShort,
LocaleFrMQ: DefaultFormatFrMQShort,
LocaleFrGF: DefaultFormatFrGFShort,
LocaleFrGP: DefaultFormatFrGPShort,
LocaleFrRE: DefaultFormatFrREShort,
LocaleDeDE: DefaultFormatDeDEShort,
LocaleHuHU: DefaultFormatHuHUShort,
LocaleItIT: DefaultFormatItITShort,
LocaleNnNO: DefaultFormatNnNOShort,
LocaleNbNO: DefaultFormatNbNOShort,
LocalePtPT: DefaultFormatPtPTShort,
LocalePtBR: DefaultFormatPtBRShort,
LocaleRoRO: DefaultFormatRoROShort,
LocaleRuRU: DefaultFormatRuRUShort,
LocaleEsES: DefaultFormatEsESShort,
LocaleCaES: DefaultFormatCaESShort,
LocaleSvSE: DefaultFormatSvSEShort,
LocaleTrTR: DefaultFormatTrTRShort,
LocaleBgBG: DefaultFormatBgBGShort,
LocaleZhCN: DefaultFormatZhCNShort,
LocaleZhTW: DefaultFormatZhTWShort,
LocaleZhHK: DefaultFormatZhHKShort,
LocaleKoKR: DefaultFormatKoKRShort,
LocaleJaJP: DefaultFormatJaJPShort,
LocaleElGR: DefaultFormatElGRShort,
LocaleCsCZ: DefaultFormatCsCZShort,
}
// DateTimeFormatsByLocale maps locales to the 'DateTime' date formats for
// all supported locales.
var DateTimeFormatsByLocale = map[Locale]string{
LocaleEnUS: DefaultFormatEnUSDateTime,
LocaleEnGB: DefaultFormatEnGBDateTime,
LocaleDaDK: DefaultFormatDaDKDateTime,
LocaleNlBE: DefaultFormatNlBEDateTime,
LocaleNlNL: DefaultFormatNlNLDateTime,
LocaleFiFI: DefaultFormatFiFIDateTime,
LocaleFrFR: DefaultFormatFrFRDateTime,
LocaleFrCA: DefaultFormatFrCADateTime,
LocaleFrGP: DefaultFormatFrGPDateTime,
LocaleFrLU: DefaultFormatFrLUDateTime,
LocaleFrMQ: DefaultFormatFrMQDateTime,
LocaleFrGF: DefaultFormatFrGFDateTime,
LocaleFrRE: DefaultFormatFrREDateTime,
LocaleDeDE: DefaultFormatDeDEDateTime,
LocaleHuHU: DefaultFormatHuHUDateTime,
LocaleItIT: DefaultFormatItITDateTime,
LocaleNnNO: DefaultFormatNnNODateTime,
LocaleNbNO: DefaultFormatNbNODateTime,
LocalePtPT: DefaultFormatPtPTDateTime,
LocalePtBR: DefaultFormatPtBRDateTime,
LocaleRoRO: DefaultFormatRoRODateTime,
LocaleRuRU: DefaultFormatRuRUDateTime,
LocaleEsES: DefaultFormatEsESDateTime,
LocaleCaES: DefaultFormatCaESDateTime,
LocaleSvSE: DefaultFormatSvSEDateTime,
LocaleTrTR: DefaultFormatTrTRDateTime,
LocaleBgBG: DefaultFormatBgBGDateTime,
LocaleZhCN: DefaultFormatZhCNDateTime,
LocaleZhTW: DefaultFormatZhTWDateTime,
LocaleZhHK: DefaultFormatZhHKDateTime,
LocaleKoKR: DefaultFormatKoKRDateTime,
LocaleJaJP: DefaultFormatJaJPDateTime,
LocaleElGR: DefaultFormatElGRDateTime,
LocaleCsCZ: DefaultFormatCsCZDateTime,
}

@ -0,0 +1,71 @@
package monday
var dayLongOrderMondayFirst = []string{
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
}
var dayLongOrderSundayFirst = []string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
var dayShortOrderMondayFirst = []string{
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun",
}
var dayShortOrderSundayFirst = []string{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
}
var monthLongOrder = []string{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
}
var monthShortOrder = []string{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
}

@ -0,0 +1,38 @@
/*
Package monday is a minimalistic translator for month and day of week names in time.Date objects
Introduction
Monday is not an alternative to standard time package. It is a temporary solution to use while
the internationalization features are not ready.
That's why monday doesn't create any additional parsing algorithms, layout identifiers. It is just
a wrapper for time.Format and time.ParseInLocation and uses all the same layout IDs, constants, etc.
Usage
Format usage:
t := time.Date(2013, 4, 12, 0, 0, 0, 0, time.UTC)
layout := "2 January 2006 15:04:05 MST"
translationEnUS := monday.Format(t, layout, monday.LocaleEnUS) // Instead of t.Format(layout)
translationRuRU := monday.Format(t, layout, monday.LocaleRuRU) // Instead of t.Format(layout)
...
Parse usage:
layout := "2 January 2006 15:04:05 MST"
// Instead of time.ParseInLocation(layout, "12 April 2013 00:00:00 MST", time.UTC)
parsed := monday.ParseInLocation(layout, "12 April 2013 00:00:00 MST", time.UTC, monday.LocaleEnUS))
parsed2 = monday.ParseInLocation(layout, "12 апреля 2013 00:00:00 MST", time.UTC, monday.LocaleRuRU))
...
Thread safety
Monday initializes all its data once in the init func and then uses only
func calls and local vars. Thus, it's thread-safe and doesn't need any mutexes to be
used with.
*/
package monday

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "bg_BG" locale: Bulgarian (Bulgaria)
// ============================================================
var longDayNamesBgBG = map[string]string{
"Sunday": "Неделя",
"Monday": "Понеделник",
"Tuesday": "Вторник",
"Wednesday": "Сряда",
"Thursday": "Четвъртък",
"Friday": "Петък",
"Saturday": "Събота",
}
var shortDayNamesBgBG = map[string]string{
"Sun": "Нд",
"Mon": "Пн",
"Tue": "Вт",
"Wed": "Ср",
"Thu": "Чt",
"Fri": "Пт",
"Sat": "Сб",
}
var longMonthNamesBgBG = map[string]string{
"January": "Януари",
"February": "Февруари",
"March": "Март",
"April": "Април",
"May": "Май",
"June": "Юни",
"July": "Юли",
"August": "Август",
"September": "Септември",
"October": "Октомври",
"November": "Ноември",
"December": "Декември",
}
var shortMonthNamesBgBG = map[string]string{
"Jan": "Яну",
"Feb": "Фев",
"Mar": "Мар",
"Apr": "Апр",
"May": "Май",
"Jun": "Юни",
"Jul": "Юли",
"Aug": "Авг",
"Sep": "Сеп",
"Oct": "Окт",
"Nov": "Ное",
"Dec": "Дек",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "ca_ES" locale: Catalan (Spain)
// ============================================================
var longDayNamesCaES = map[string]string{
"Sunday": "diumenge",
"Monday": "dilluns",
"Tuesday": "dimarts",
"Wednesday": "dimecres",
"Thursday": "dijous",
"Friday": "divendres",
"Saturday": "dissabte",
}
var shortDayNamesCaES = map[string]string{
"Sun": "dg",
"Mon": "dl",
"Tue": "dt",
"Wed": "dc",
"Thu": "dj",
"Fri": "dv",
"Sat": "ds",
}
var longMonthNamesCaES = map[string]string{
"January": "gener",
"February": "febrer",
"March": "març",
"April": "abril",
"May": "maig",
"June": "juny",
"July": "juliol",
"August": "agost",
"September": "setembre",
"October": "octubre",
"November": "novembre",
"December": "desembre",
}
var shortMonthNamesCaES = map[string]string{
"Jan": "gen",
"Feb": "febr",
"Mar": "març",
"Apr": "abr",
"May": "maig",
"Jun": "juny",
"Jul": "jul",
"Aug": "ag",
"Sep": "set",
"Oct": "oct",
"Nov": "nov",
"Dec": "des",
}

@ -0,0 +1,183 @@
package monday
import "strings"
func findInString(where string, what string, foundIndex *int, trimRight *int) (found bool) {
ind := strings.Index(strings.ToLower(where), strings.ToLower(what))
if ind != -1 {
*foundIndex = ind
*trimRight = len(where) - ind - len(what)
return true
}
return false
}
// commonFormatFunc is used for languages which don't have changed forms of month names dependent
// on their position (after day or standalone)
func commonFormatFunc(value, format string,
knownDaysShort, knownDaysLong, knownMonthsShort, knownMonthsLong, knownPeriods map[string]string) string {
l := stringToLayoutItems(value)
f := stringToLayoutItems(format)
if len(l) != len(f) {
return value // layouts does not matches
}
sb := &strings.Builder{}
sb.Grow(32) // Reasonable default size that should fit most strings.
for i, v := range l {
var knw map[string]string
// number of symbols before replaced term
foundIndex := 0
trimRight := 0
lowerCase := false
switch {
case findInString(f[i].item, "Monday", &foundIndex, &trimRight):
knw = knownDaysLong
case findInString(f[i].item, "Mon", &foundIndex, &trimRight):
knw = knownDaysShort
case findInString(f[i].item, "January", &foundIndex, &trimRight):
knw = knownMonthsLong
case findInString(f[i].item, "Jan", &foundIndex, &trimRight):
knw = knownMonthsShort
case findInString(f[i].item, "PM", &foundIndex, &trimRight):
knw = knownPeriods
case findInString(f[i].item, "pm", &foundIndex, &trimRight):
lowerCase = true
knw = knownPeriods
}
knw = mapToLowerCase(knw)
if knw != nil {
trimmedItem := strings.ToLower(v.item[foundIndex : len(v.item)-trimRight])
tr, ok := knw[trimmedItem]
if lowerCase == true {
tr = strings.ToLower(tr)
}
if ok {
sb.WriteString(v.item[:foundIndex])
sb.WriteString(tr)
sb.WriteString(v.item[len(v.item)-trimRight:])
} else {
sb.WriteString(v.item)
}
} else {
sb.WriteString(v.item)
}
}
return sb.String()
}
func hasDigitBefore(l []dateStringLayoutItem, position int) bool {
if position >= 2 {
return l[position-2].isDigit && len(l[position-2].item) <= 2
}
return false
}
// commonGenitiveFormatFunc is used for languages with genitive forms of names, like Russian.
func commonGenitiveFormatFunc(value, format string,
knownDaysShort, knownDaysLong, knownMonthsShort, knownMonthsLong,
knownMonthsGenShort, knownMonthsGenLong, knownPeriods map[string]string) string {
l := stringToLayoutItems(value)
f := stringToLayoutItems(format)
if len(l) != len(f) {
return value // layouts does not matches
}
sb := &strings.Builder{}
sb.Grow(32) // Reasonable default size that should fit most strings.
for i, v := range l {
lowerCase := false
var knw map[string]string
switch f[i].item {
case "Mon":
knw = knownDaysShort
case "Monday":
knw = knownDaysLong
case "Jan":
if hasDigitBefore(l, i) {
knw = knownMonthsGenShort
} else {
knw = knownMonthsShort
}
case "January":
if hasDigitBefore(l, i) {
knw = knownMonthsGenLong
} else {
knw = knownMonthsLong
}
case "PM":
knw = knownPeriods
case "pm":
lowerCase = true
knw = knownPeriods
}
knw = mapToLowerCase(knw)
if knw != nil {
tr, ok := knw[strings.ToLower(v.item)]
if !ok {
sb.WriteString(v.item)
continue
}
if lowerCase == true {
tr = strings.ToLower(tr)
}
sb.WriteString(tr)
} else {
sb.WriteString(v.item)
}
}
return sb.String()
}
func createCommonFormatFunc(locale Locale) internalFormatFunc {
return func(value, layout string) (res string) {
return commonFormatFunc(value, layout,
knownDaysShort[locale], knownDaysLong[locale], knownMonthsShort[locale], knownMonthsLong[locale], knownPeriods[locale])
}
}
func createCommonFormatFuncWithGenitive(locale Locale) internalFormatFunc {
return func(value, layout string) (res string) {
return commonGenitiveFormatFunc(value, layout,
knownDaysShort[locale], knownDaysLong[locale], knownMonthsShort[locale], knownMonthsLong[locale],
knownMonthsGenitiveShort[locale], knownMonthsGenitiveLong[locale], knownPeriods[locale])
}
}
func createCommonParseFunc(locale Locale) internalParseFunc {
return func(layout, value string) string {
return commonFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale], knownPeriodsReverse[locale])
}
}
func createCommonParsetFuncWithGenitive(locale Locale) internalParseFunc {
return func(layout, value string) (res string) {
return commonGenitiveFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale],
knownMonthsGenitiveShortReverse[locale], knownMonthsGenitiveLongReverse[locale], knownPeriodsReverse[locale])
}
}
func mapToLowerCase(source map[string]string) map[string]string {
result := make(map[string]string, len(source))
for k, v := range source {
result[strings.ToLower(k)] = v
}
return result
}

@ -0,0 +1,85 @@
package monday
// ============================================================
// Format rules for "cs_CZ" locale: Czech (Czech Republic)
// ============================================================
var longDayNamesCsCZ = map[string]string{
"Sunday": "neděle",
"Monday": "pondělí",
"Tuesday": "úterý",
"Wednesday": "středa",
"Thursday": "čtvrtek",
"Friday": "pátek",
"Saturday": "sobota",
}
var shortDayNamesCsCZ = map[string]string{
"Sun": "ne",
"Mon": "po",
"Tue": "út",
"Wed": "st",
"Thu": "čt",
"Fri": "pá",
"Sat": "so",
}
var longMonthNamesCsCZ = map[string]string{
"January": "leden",
"February": "únor",
"March": "březen",
"April": "duben",
"May": "květen",
"June": "červen",
"July": "červenec",
"August": "srpen",
"September": "září",
"October": "říjen",
"November": "listopad",
"December": "prosinec",
}
var shortMonthNamesCsCZ = map[string]string{
"Jan": "led",
"Feb": "úno",
"Mar": "bře",
"Apr": "dub",
"May": "kvě",
"Jun": "čvn",
"Jul": "čvc",
"Aug": "srp",
"Sep": "zář",
"Oct": "říj",
"Nov": "lis",
"Dec": "pro",
}
var longMonthNamesGenitiveCsCZ = map[string]string{
"January": "ledna",
"February": "února",
"March": "března",
"April": "dubna",
"May": "května",
"June": "června",
"July": "července",
"August": "srpna",
"September": "září",
"October": "října",
"November": "listopadu",
"December": "prosince",
}
var shortMonthNamesGenitiveCsCZ = map[string]string{
"Jan": "led",
"Feb": "úno",
"Mar": "bře",
"Apr": "dub",
"May": "kvě",
"Jun": "čvn",
"Jul": "čvc",
"Aug": "srp",
"Sep": "zář",
"Oct": "říj",
"Nov": "lis",
"Dec": "pro",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "da_DK" locale: Danish (Denmark)
// ============================================================
var longDayNamesDaDK = map[string]string{
"Sunday": "søndag",
"Monday": "mandag",
"Tuesday": "tirsdag",
"Wednesday": "onsdag",
"Thursday": "torsdag",
"Friday": "fredag",
"Saturday": "lørdag",
}
var shortDayNamesDaDK = map[string]string{
"Sun": "søn",
"Mon": "man",
"Tue": "tir",
"Wed": "ons",
"Thu": "tor",
"Fri": "fre",
"Sat": "lør",
}
var longMonthNamesDaDK = map[string]string{
"January": "januar",
"February": "februar",
"March": "marts",
"April": "april",
"May": "maj",
"June": "juni",
"July": "juli",
"August": "august",
"September": "september",
"October": "oktober",
"November": "november",
"December": "december",
}
var shortMonthNamesDaDK = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "maj",
"Jun": "jun",
"Jul": "jul",
"Aug": "aug",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "de_DE" locale: German (Germany)
// ============================================================
var longDayNamesDeDE = map[string]string{
"Sunday": "Sonntag",
"Monday": "Montag",
"Tuesday": "Dienstag",
"Wednesday": "Mittwoch",
"Thursday": "Donnerstag",
"Friday": "Freitag",
"Saturday": "Samstag",
}
var shortDayNamesDeDE = map[string]string{
"Sun": "So",
"Mon": "Mo",
"Tue": "Di",
"Wed": "Mi",
"Thu": "Do",
"Fri": "Fr",
"Sat": "Sa",
}
var longMonthNamesDeDE = map[string]string{
"January": "Januar",
"February": "Februar",
"March": "März",
"April": "April",
"May": "Mai",
"June": "Juni",
"July": "Juli",
"August": "August",
"September": "September",
"October": "Oktober",
"November": "November",
"December": "Dezember",
}
var shortMonthNamesDeDE = map[string]string{
"Jan": "Jan",
"Feb": "Feb",
"Mar": "Mär",
"Apr": "Apr",
"May": "Mai",
"Jun": "Juni",
"Jul": "Juli",
"Aug": "Aug",
"Sep": "Sep",
"Oct": "Okt",
"Nov": "Nov",
"Dec": "Dez",
}

@ -0,0 +1,77 @@
package monday
// ============================================================
// Format rules for "el_GR" locale: Greek (Greece)
// ============================================================
var longDayNamesElGR = map[string]string{
"Sunday": "Κυριακή",
"Monday": "Δευτέρα",
"Tuesday": "Τρίτη",
"Wednesday": "Τετάρτη",
"Thursday": "Πέμπτη",
"Friday": "Παρασκευή",
"Saturday": "Σάββατο",
}
var shortDayNamesElGR = map[string]string{
"Sun": "Κυρ",
"Mon": "Δευ",
"Tue": "Τρι",
"Wed": "Τετ",
"Thu": "Πεμ",
"Fri": "Παρ",
"Sat": "Σαβ",
}
var longMonthNamesElGR = map[string]string{
"January": "Ιανουάριος",
"February": "Φεβρουάριος",
"March": "Μάρτιος",
"April": "Απρίλιος",
"May": "Μάιος",
"June": "Ιούνιος",
"July": "Ιούλιος",
"August": "Αύγουστος",
"September": "Σεπτέμβριος",
"October": "Οκτώβριος",
"November": "Νοέμβριος",
"December": "Δεκέμβριος",
}
var longMonthNamesGenitiveElGR = map[string]string{
"January": "Ιανουαρίου",
"February": "Φεβρουαρίου",
"March": "Μαρτίου",
"April": "Απριλίου",
"May": "Μαΐου",
"June": "Ιουνίου",
"July": "Ιουλίου",
"August": "Αυγούστου",
"September": "Σεπτεμβρίου",
"October": "Οκτωβρίου",
"November": "Νοεμβρίου",
"December": "Δεκεμβρίου",
}
var shortMonthNamesElGR = map[string]string{
"Jan": "Ιαν",
"Feb": "Φεβ",
"Mar": "Μαρ",
"Apr": "Απρ",
"May": "Μαϊ",
"Jun": "Ιουν",
"Jul": "Ιουλ",
"Aug": "Αυγ",
"Sep": "Σεπ",
"Oct": "Οκτ",
"Nov": "Νοε",
"Dec": "Δεκ",
}
var periodsElGR = map[string]string{
"am": "πμ",
"pm": "μμ",
"AM": "ΠΜ",
"PM": "ΜΜ",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "en_GB" locale: English (Great Britain)
// ============================================================
var longDayNamesEnGB = map[string]string{
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
"Wednesday": "Wednesday",
"Thursday": "Thursday",
"Friday": "Friday",
"Saturday": "Saturday",
}
var shortDayNamesEnGB = map[string]string{
"Sun": "Sun",
"Mon": "Mon",
"Tue": "Tue",
"Wed": "Wed",
"Thu": "Thu",
"Fri": "Fri",
"Sat": "Sat",
}
var longMonthNamesEnGB = map[string]string{
"January": "January",
"February": "February",
"March": "March",
"April": "April",
"May": "May",
"June": "June",
"July": "July",
"August": "August",
"September": "September",
"October": "October",
"November": "November",
"December": "December",
}
var shortMonthNamesEnGB = map[string]string{
"Jan": "Jan",
"Feb": "Feb",
"Mar": "Mar",
"Apr": "Apr",
"May": "May",
"Jun": "Jun",
"Jul": "Jul",
"Aug": "Aug",
"Sep": "Sep",
"Oct": "Oct",
"Nov": "Nov",
"Dec": "Dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "en_US" locale: English (United States)
// ============================================================
var longDayNamesEnUS = map[string]string{
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
"Wednesday": "Wednesday",
"Thursday": "Thursday",
"Friday": "Friday",
"Saturday": "Saturday",
}
var shortDayNamesEnUS = map[string]string{
"Sun": "Sun",
"Mon": "Mon",
"Tue": "Tue",
"Wed": "Wed",
"Thu": "Thu",
"Fri": "Fri",
"Sat": "Sat",
}
var longMonthNamesEnUS = map[string]string{
"January": "January",
"February": "February",
"March": "March",
"April": "April",
"May": "May",
"June": "June",
"July": "July",
"August": "August",
"September": "September",
"October": "October",
"November": "November",
"December": "December",
}
var shortMonthNamesEnUS = map[string]string{
"Jan": "Jan",
"Feb": "Feb",
"Mar": "Mar",
"Apr": "Apr",
"May": "May",
"Jun": "Jun",
"Jul": "Jul",
"Aug": "Aug",
"Sep": "Sep",
"Oct": "Oct",
"Nov": "Nov",
"Dec": "Dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "es_ES" locale: Spanish (Spain)
// ============================================================
var longDayNamesEsES = map[string]string{
"Sunday": "domingo",
"Monday": "lunes",
"Tuesday": "martes",
"Wednesday": "miércoles",
"Thursday": "jueves",
"Friday": "viernes",
"Saturday": "sábado",
}
var shortDayNamesEsES = map[string]string{
"Sun": "dom",
"Mon": "lun",
"Tue": "mar",
"Wed": "mié",
"Thu": "jue",
"Fri": "vie",
"Sat": "sáb",
}
var longMonthNamesEsES = map[string]string{
"January": "enero",
"February": "febrero",
"March": "marzo",
"April": "abril",
"May": "mayo",
"June": "junio",
"July": "julio",
"August": "agosto",
"September": "septiembre",
"October": "octubre",
"November": "noviembre",
"December": "diciembre",
}
var shortMonthNamesEsES = map[string]string{
"Jan": "ene",
"Feb": "feb",
"Mar": "mar",
"Apr": "abr",
"May": "may",
"Jun": "jun",
"Jul": "jul",
"Aug": "ago",
"Sep": "sep",
"Oct": "oct",
"Nov": "nov",
"Dec": "dic",
}

@ -0,0 +1,70 @@
package monday
// ============================================================
// Format rules for "fi_FI" locale: Finnish (Finland)
// ============================================================
var longDayNamesFiFI = map[string]string{
"Sunday": "sunnuntai",
"Monday": "maanantai",
"Tuesday": "tiistai",
"Wednesday": "keskiviikko",
"Thursday": "torstai",
"Friday": "perjantai",
"Saturday": "lauantai",
}
var shortDayNamesFiFI = map[string]string{
"Sun": "su",
"Mon": "ma",
"Tue": "ti",
"Wed": "ke",
"Thu": "to",
"Fri": "pe",
"Sat": "la",
}
var longMonthNamesFiFI = map[string]string{
"January": "tammikuu",
"February": "helmikuu",
"March": "maaliskuu",
"April": "huhtikuu",
"May": "toukokuu",
"June": "kesäkuu",
"July": "heinäkuu",
"August": "elokuu",
"September": "syyskuu",
"October": "lokakuu",
"November": "marraskuu",
"December": "joulukuu",
}
var longMonthNamesGenitiveFiFI = map[string]string{
"January": "tammikuuta",
"February": "helmikuuta",
"March": "maaliskuuta",
"April": "huhtikuuta",
"May": "toukokuuta",
"June": "kesäkuuta",
"July": "heinäkuuta",
"August": "elokuuta",
"September": "syyskuuta",
"October": "lokakuuta",
"November": "marraskuuta",
"December": "joulukuuta",
}
var shortMonthNamesFiFI = map[string]string{
"Jan": "tammi",
"Feb": "helmi",
"Mar": "maalis",
"Apr": "huhti",
"May": "touko",
"Jun": "kesä",
"Jul": "heinä",
"Aug": "elo",
"Sep": "syys",
"Oct": "loka",
"Nov": "marras",
"Dec": "joulu",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "fr_FR" locale: French (France)
// ============================================================
var longDayNamesFrFR = map[string]string{
"Sunday": "dimanche",
"Monday": "lundi",
"Tuesday": "mardi",
"Wednesday": "mercredi",
"Thursday": "jeudi",
"Friday": "vendredi",
"Saturday": "samedi",
}
var shortDayNamesFrFR = map[string]string{
"Sun": "dim",
"Mon": "lun",
"Tue": "mar",
"Wed": "mer",
"Thu": "jeu",
"Fri": "ven",
"Sat": "sam",
}
var longMonthNamesFrFR = map[string]string{
"January": "janvier",
"February": "février",
"March": "mars",
"April": "avril",
"May": "mai",
"June": "juin",
"July": "juillet",
"August": "août",
"September": "septembre",
"October": "octobre",
"November": "novembre",
"December": "décembre",
}
var shortMonthNamesFrFR = map[string]string{
"Jan": "janv",
"Feb": "févr",
"Mar": "mars",
"Apr": "avr",
"May": "mai",
"Jun": "juin",
"Jul": "juil",
"Aug": "août",
"Sep": "sept",
"Oct": "oct",
"Nov": "nov",
"Dec": "déc",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "hu_HU" locale: Hungarian (Hungary)
// ============================================================
var longDayNamesHuHU = map[string]string{
"Sunday": "vasárnap",
"Monday": "hétfő",
"Tuesday": "kedd",
"Wednesday": "szerda",
"Thursday": "csütörtök",
"Friday": "péntek",
"Saturday": "szombat",
}
var shortDayNamesHuHU = map[string]string{
"Sun": "V",
"Mon": "H",
"Tue": "K",
"Wed": "Sze",
"Thu": "Cs",
"Fri": "P",
"Sat": "Szo",
}
var longMonthNamesHuHU = map[string]string{
"January": "január",
"February": "február",
"March": "március",
"April": "április",
"May": "május",
"June": "június",
"July": "július",
"August": "augusztus",
"September": "szeptember",
"October": "október",
"November": "november",
"December": "december",
}
var shortMonthNamesHuHU = map[string]string{
"Jan": "jan",
"Feb": "febr",
"Mar": "márc",
"Apr": "ápr",
"May": "máj",
"Jun": "jún",
"Jul": "júl",
"Aug": "aug",
"Sep": "szept",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "id_ID" locale: Indonesian (Indonesia)
// ============================================================
var longDayNamesIdID = map[string]string{
"Sunday": "Minggu",
"Monday": "Senin",
"Tuesday": "Selasa",
"Wednesday": "Rabu",
"Thursday": "Kamis",
"Friday": "Jumat",
"Saturday": "Sabtu",
}
var shortDayNamesIdID = map[string]string{
"Sun": "Min",
"Mon": "Sen",
"Tue": "Sel",
"Wed": "Rab",
"Thu": "Kam",
"Fri": "Jum",
"Sat": "Sab",
}
var longMonthNamesIdID = map[string]string{
"January": "Januari",
"February": "Februari",
"March": "Maret",
"April": "April",
"May": "Mei",
"June": "Juni",
"July": "Juli",
"August": "Agustus",
"September": "September",
"October": "Oktober",
"November": "November",
"December": "Desember",
}
var shortMonthNamesIdID = map[string]string{
"Jan": "Jan",
"Feb": "Feb",
"Mar": "Mar",
"Apr": "Apr",
"May": "Mei",
"Jun": "Jun",
"Jul": "Jul",
"Aug": "Ags",
"Sep": "Sep",
"Oct": "Okt",
"Nov": "Nov",
"Dec": "Des",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "it_IT" locale: Italian (Italy)
// ============================================================
var longDayNamesItIT = map[string]string{
"Sunday": "domenica",
"Monday": "lunedì",
"Tuesday": "martedì",
"Wednesday": "mercoledì",
"Thursday": "giovedì",
"Friday": "venerdì",
"Saturday": "sabato",
}
var shortDayNamesItIT = map[string]string{
"Sun": "dom",
"Mon": "lun",
"Tue": "mar",
"Wed": "mer",
"Thu": "gio",
"Fri": "ven",
"Sat": "sab",
}
var longMonthNamesItIT = map[string]string{
"January": "gennaio",
"February": "febbraio",
"March": "marzo",
"April": "aprile",
"May": "maggio",
"June": "giugno",
"July": "luglio",
"August": "agosto",
"September": "settembre",
"October": "ottobre",
"November": "novembre",
"December": "dicembre",
}
var shortMonthNamesItIT = map[string]string{
"Jan": "gen",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "mag",
"Jun": "giu",
"Jul": "lug",
"Aug": "ago",
"Sep": "set",
"Oct": "ott",
"Nov": "nov",
"Dec": "dic",
}

@ -0,0 +1,95 @@
package monday
import (
"strings"
)
// ============================================================
// Format rules for "ja_JP" locale: Japanese
// ============================================================
var longDayNamesJaJP = map[string]string{
"Sunday": "日曜日",
"Monday": "月曜日",
"Tuesday": "火曜日",
"Wednesday": "水曜日",
"Thursday": "木曜日",
"Friday": "金曜日",
"Saturday": "土曜日",
}
var shortDayNamesJaJP = map[string]string{
"Sun": "日",
"Mon": "月",
"Tue": "火",
"Wed": "水",
"Thu": "木",
"Fri": "金",
"Sat": "土",
}
var longMonthNamesJaJP = map[string]string{
"January": "1月",
"February": "2月",
"March": "3月",
"April": "4月",
"May": "5月",
"June": "6月",
"July": "7月",
"August": "8月",
"September": "9月",
"October": "10月",
"November": "11月",
"December": "12月",
}
var shortMonthNamesJaJP = map[string]string{
"Jan": "1月",
"Feb": "2月",
"Mar": "3月",
"Apr": "4月",
"May": "5月",
"Jun": "6月",
"Jul": "7月",
"Aug": "8月",
"Sep": "9月",
"Oct": "10月",
"Nov": "11月",
"Dec": "12月",
}
var periodsJaJP = map[string]string{
"am": "午前",
"pm": "午後",
"AM": "午前",
"PM": "午後",
}
func parseFuncJaCommon(locale Locale) internalParseFunc {
return func(layout, value string) string {
// This special case is needed because ja_JP... contains month names
// that consist of a number, a delimiter, and '月'. Example: "October" = "10 月"
//
// This means that probably default time package layout IDs like 'January' or 'Jan'
// shouldn't be used in ja_JP. But this is a time-compatible package, so someone
// might actually use those and we need to replace those before doing standard procedures.
for k, v := range knownMonthsLongReverse[locale] {
value = strings.Replace(value, k, v, -1)
}
value = commonFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale], knownPeriods[locale])
// knownPeriodsReverse has hash collisions
for k, v := range knownPeriodsReverse[locale] {
targetValue := strings.ToLower(v)
if strings.Index(layout, "PM") != -1 {
targetValue = strings.ToUpper(v)
}
value = strings.Replace(value, k, targetValue, -1)
}
return value
}
}

@ -0,0 +1,94 @@
package monday
import "strings"
// ============================================================
// Format rules for "ko_KR" locale: Korean (Korea)
// ============================================================
var longDayNamesKoKR = map[string]string{
"Sunday": "일요일",
"Monday": "월요일",
"Tuesday": "화요일",
"Wednesday": "수요일",
"Thursday": "목요일",
"Friday": "금요일",
"Saturday": "토요일",
}
var shortDayNamesKoKR = map[string]string{
"Sun": "일",
"Mon": "월",
"Tue": "화",
"Wed": "수",
"Thu": "목",
"Fri": "금",
"Sat": "토",
}
var longMonthNamesKoKR = map[string]string{
"January": "1월",
"February": "2월",
"March": "3월",
"April": "4월",
"May": "5월",
"June": "6월",
"July": "7월",
"August": "8월",
"September": "9월",
"October": "10월",
"November": "11월",
"December": "12월",
}
var shortMonthNamesKoKR = map[string]string{
"Jan": "1월",
"Feb": "2월",
"Mar": "3월",
"Apr": "4월",
"May": "5월",
"Jun": "6월",
"Jul": "7월",
"Aug": "8월",
"Sep": "9월",
"Oct": "10월",
"Nov": "11월",
"Dec": "12월",
}
var periodsKoKR = map[string]string{
"am": "오전",
"pm": "오후",
"AM": "오전",
"PM": "오후",
}
func parseFuncKoCommon(locale Locale) internalParseFunc {
return func(layout, value string) string {
// This special case is needed because ko_KR... contains month names
// that consist of a number, a delimiter, and '월'. Example: "September" = "9 월"
//
// This means that probably default time package layout IDs like 'January' or 'Jan'
// shouldn't be used in ko_KR. But this is a time-compatible package, so someone
// might actually use those and we need to replace those before doing standard procedures.
for k, v := range knownMonthsLongReverse[locale] {
value = strings.Replace(value, k, v, -1)
}
value = commonFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale], knownPeriods[locale])
// knownPeriodsReverse has hash collisions
for k, v := range knownPeriodsReverse[locale] {
targetValue := strings.ToLower(v)
if strings.Index(layout, "PM") != -1 {
targetValue = strings.ToUpper(v)
}
value = strings.Replace(value, k, targetValue, -1)
}
return value
}
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "nb_NO" locale: Norwegian Bokmål (Norway)
// ============================================================
var longDayNamesNbNO = map[string]string{
"Sunday": "søndag",
"Monday": "mandag",
"Tuesday": "tirsdag",
"Wednesday": "onsdag",
"Thursday": "torsdag",
"Friday": "fredag",
"Saturday": "lørdag",
}
var shortDayNamesNbNO = map[string]string{
"Sun": "sø",
"Mon": "ma",
"Tue": "ti",
"Wed": "on",
"Thu": "to",
"Fri": "fr",
"Sat": "lø",
}
var longMonthNamesNbNO = map[string]string{
"January": "januar",
"February": "februar",
"March": "mars",
"April": "april",
"May": "mai",
"June": "juni",
"July": "juli",
"August": "august",
"September": "september",
"October": "oktober",
"November": "november",
"December": "desember",
}
var shortMonthNamesNbNO = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "mai",
"Jun": "jun",
"Jul": "jul",
"Aug": "aug",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "des",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "nl_BE" locale: Dutch (Belgium)
// ============================================================
var longDayNamesNlBE = map[string]string{
"Sunday": "zondag",
"Monday": "maandag",
"Tuesday": "dinsdag",
"Wednesday": "woensdag",
"Thursday": "donderdag",
"Friday": "vrijdag",
"Saturday": "zaterdag",
}
var shortDayNamesNlBE = map[string]string{
"Sun": "zo",
"Mon": "ma",
"Tue": "di",
"Wed": "wo",
"Thu": "do",
"Fri": "vr",
"Sat": "za",
}
var longMonthNamesNlBE = map[string]string{
"January": "januari",
"February": "februari",
"March": "maart",
"April": "april",
"May": "mei",
"June": "juni",
"July": "juli",
"August": "augustus",
"September": "september",
"October": "oktober",
"November": "november",
"December": "december",
}
var shortMonthNamesNlBE = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mrt",
"Apr": "apr",
"May": "mei",
"Jun": "jun",
"Jul": "jul",
"Aug": "aug",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "nn_NO" locale: Norwegian Nynorsk (Norway)
// ============================================================
var longDayNamesNnNO = map[string]string{
"Sunday": "søndag",
"Monday": "måndag",
"Tuesday": "tysdag",
"Wednesday": "onsdag",
"Thursday": "torsdag",
"Friday": "fredag",
"Saturday": "laurdag",
}
var shortDayNamesNnNO = map[string]string{
"Sun": "sø",
"Mon": "må",
"Tue": "ty",
"Wed": "on",
"Thu": "to",
"Fri": "fr",
"Sat": "la",
}
var longMonthNamesNnNO = map[string]string{
"January": "januar",
"February": "februar",
"March": "mars",
"April": "april",
"May": "mai",
"June": "juni",
"July": "juli",
"August": "august",
"September": "september",
"October": "oktober",
"November": "november",
"December": "desember",
}
var shortMonthNamesNnNO = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mars",
"Apr": "apr",
"May": "mai",
"Jun": "juni",
"Jul": "juli",
"Aug": "aug",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "des",
}

@ -0,0 +1,56 @@
package monday
// ============================================================
// Format rules for "pl_PL" locale: Polish (Poland)
// ============================================================
var longDayNamesPlPL = map[string]string{
"Sunday": "Niedziela",
"Monday": "Poniedziałek",
"Tuesday": "Wtorek",
"Wednesday": "Środa",
"Thursday": "Czwartek",
"Friday": "Piątek",
"Saturday": "Sobota",
}
// http://sjp.pwn.pl/poradnia/haslo/skracanie-nazw-dni-tygodnia-i-miesiecy;8124.html
var shortDayNamesPlPL = map[string]string{
"Sun": "Nie",
"Mon": "Pon",
"Tue": "Wto",
"Wed": "Śro",
"Thu": "Czw",
"Fri": "Pią",
"Sat": "Sob",
}
var longMonthNamesPlPL = map[string]string{
"January": "Styczeń",
"February": "Luty",
"March": "Marzec",
"April": "Kwiecień",
"May": "Maj",
"June": "Czerwiec",
"July": "Lipiec",
"August": "Sierpień",
"September": "Wrzesień",
"October": "Październik",
"November": "Listopad",
"December": "Grudzień",
}
var shortMonthNamesPlPL = map[string]string{
"Jan": "Sty",
"Feb": "Lut",
"Mar": "Mar",
"Apr": "Kwi",
"May": "Maj",
"Jun": "Cze",
"Jul": "Lip",
"Aug": "Sie",
"Sep": "Wrz",
"Oct": "Paź",
"Nov": "Lis",
"Dec": "Gru",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "pt_BR" locale: Portuguese (Brazil)
// ============================================================
var longDayNamesPtBR = map[string]string{
"Sunday": "domingo",
"Monday": "segunda-feira",
"Tuesday": "terça-feira",
"Wednesday": "quarta-feira",
"Thursday": "quinta-feira",
"Friday": "sexta-feira",
"Saturday": "sábado",
}
var shortDayNamesPtBR = map[string]string{
"Sun": "dom",
"Mon": "seg",
"Tue": "ter",
"Wed": "qua",
"Thu": "qui",
"Fri": "sex",
"Sat": "sáb",
}
var longMonthNamesPtBR = map[string]string{
"January": "janeiro",
"February": "fevereiro",
"March": "março",
"April": "abril",
"May": "maio",
"June": "junho",
"July": "julho",
"August": "agosto",
"September": "setembro",
"October": "outubro",
"November": "novembro",
"December": "dezembro",
}
var shortMonthNamesPtBR = map[string]string{
"Jan": "jan",
"Feb": "fev",
"Mar": "mar",
"Apr": "abr",
"May": "mai",
"Jun": "jun",
"Jul": "jul",
"Aug": "ago",
"Sep": "set",
"Oct": "out",
"Nov": "nov",
"Dec": "dez",
}

@ -0,0 +1,72 @@
package monday
import "strings"
// ============================================================
// Format rules for "pt_PT" locale: Portuguese (Portugal)
// ============================================================
var longDayNamesPtPT = map[string]string{
"Sunday": "Domingo",
"Monday": "Segunda-feira",
"Tuesday": "Terça-feira",
"Wednesday": "Quarta-feira",
"Thursday": "Quinta-feira",
"Friday": "Sexta-feira",
"Saturday": "Sábado",
}
var shortDayNamesPtPT = map[string]string{
"Sun": "dom",
"Mon": "seg",
"Tue": "ter",
"Wed": "qua",
"Thu": "qui",
"Fri": "sex",
"Sat": "sáb",
}
var longMonthNamesPtPT = map[string]string{
"January": "Janeiro",
"February": "Fevereiro",
"March": "Março",
"April": "Abril",
"May": "Maio",
"June": "Junho",
"July": "Julho",
"August": "Agosto",
"September": "Setembro",
"October": "Outubro",
"November": "Novembro",
"December": "Dezembro",
}
var shortMonthNamesPtPT = map[string]string{
"Jan": "Jan",
"Feb": "Fev",
"Mar": "Mar",
"Apr": "Abr",
"May": "Mai",
"Jun": "Jun",
"Jul": "Jul",
"Aug": "Ago",
"Sep": "Set",
"Oct": "Out",
"Nov": "Nov",
"Dec": "Dez",
}
func parseFuncPtCommon(locale Locale) internalParseFunc {
return func(layout, value string) string {
// This special case is needed because Pt_PT/Pt_BR/... contains day-of-week names
// that consist of two words and a delimiter (like 'terça-feira'). These
// should be replaced before using the standard procedure correctly.
for k, v := range knownDaysLongReverse[locale] {
value = strings.Replace(value, k, v, -1)
}
return commonFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale], knownPeriods[locale])
}
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "ro_RO" locale: Romanian (Romania)
// ============================================================
var longDayNamesRoRO = map[string]string{
"Sunday": "duminică",
"Monday": "luni",
"Tuesday": "marți",
"Wednesday": "miercuri",
"Thursday": "joi",
"Friday": "vineri",
"Saturday": "sâmbătă",
}
var shortDayNamesRoRO = map[string]string{
"Sun": "Du",
"Mon": "Lu",
"Tue": "Ma",
"Wed": "Mi",
"Thu": "Jo",
"Fri": "Vi",
"Sat": "Sâ",
}
var longMonthNamesRoRO = map[string]string{
"January": "ianuarie",
"February": "februarie",
"March": "martie",
"April": "aprilie",
"May": "mai",
"June": "iunie",
"July": "iulie",
"August": "august",
"September": "septembrie",
"October": "octombrie",
"November": "noiembrie",
"December": "decembrie",
}
var shortMonthNamesRoRO = map[string]string{
"Jan": "ian",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "mai",
"Jun": "iun",
"Jul": "iul",
"Aug": "aug",
"Sep": "sept",
"Oct": "oct",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,85 @@
package monday
// ============================================================
// Format rules for "ru_RU" locale: Russian (Russia)
// ============================================================
var longDayNamesRuRU = map[string]string{
"Sunday": "Воскресенье",
"Monday": "Понедельник",
"Tuesday": "Вторник",
"Wednesday": "Среда",
"Thursday": "Четверг",
"Friday": "Пятница",
"Saturday": "Суббота",
}
var shortDayNamesRuRU = map[string]string{
"Sun": "Вс",
"Mon": "Пн",
"Tue": "Вт",
"Wed": "Ср",
"Thu": "Чт",
"Fri": "Пт",
"Sat": "Сб",
}
var longMonthNamesRuRU = map[string]string{
"January": "Январь",
"February": "Февраль",
"March": "Март",
"April": "Апрель",
"May": "Май",
"June": "Июнь",
"July": "Июль",
"August": "Август",
"September": "Сентябрь",
"October": "Октябрь",
"November": "Ноябрь",
"December": "Декабрь",
}
var longMonthNamesGenitiveRuRU = map[string]string{
"January": "января",
"February": "февраля",
"March": "марта",
"April": "апреля",
"May": "мая",
"June": "июня",
"July": "июля",
"August": "августа",
"September": "сентября",
"October": "октября",
"November": "ноября",
"December": "декабря",
}
var shortMonthNamesRuRU = map[string]string{
"Jan": "Янв",
"Feb": "Фев",
"Mar": "Мар",
"Apr": "Апр",
"May": "Май",
"Jun": "Июн",
"Jul": "Июл",
"Aug": "Авг",
"Sep": "Сен",
"Oct": "Окт",
"Nov": "Ноя",
"Dec": "Дек",
}
var shortMonthNamesGenitiveRuRU = map[string]string{
"Jan": "янв",
"Feb": "фев",
"Mar": "мар",
"Apr": "апр",
"May": "мая",
"Jun": "июн",
"Jul": "июл",
"Aug": "авг",
"Sep": "сен",
"Oct": "окт",
"Nov": "ноя",
"Dec": "дек",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "sl_SI" locale: Slovenian
// ============================================================
var longDayNamesSlSI = map[string]string{
"Sunday": "nedelja",
"Monday": "ponedeljek",
"Tuesday": "toret",
"Wednesday": "sreda",
"Thursday": "četrtek",
"Friday": "petek",
"Saturday": "sobota",
}
var shortDayNamesSlSI = map[string]string{
"Sun": "ned",
"Mon": "pon",
"Tue": "tor",
"Wed": "sre",
"Thu": "čet",
"Fri": "pet",
"Sat": "sob",
}
var longMonthNamesSlSI = map[string]string{
"January": "januar",
"February": "februar",
"March": "marec",
"April": "april",
"May": "maj",
"June": "junij",
"July": "julij",
"August": "avgust",
"September": "september",
"October": "oktober",
"November": "november",
"December": "december",
}
var shortMonthNamesSlSI = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "maj",
"Jun": "jun",
"Jul": "jul",
"Aug": "avg",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "sv_SE" locale: Swedish (Sweden)
// ============================================================
var longDayNamesSvSE = map[string]string{
"Sunday": "söndag",
"Monday": "måndag",
"Tuesday": "tisdag",
"Wednesday": "onsdag",
"Thursday": "torsdag",
"Friday": "fredag",
"Saturday": "lördag",
}
var shortDayNamesSvSE = map[string]string{
"Sun": "sön",
"Mon": "mån",
"Tue": "tis",
"Wed": "ons",
"Thu": "tors",
"Fri": "fre",
"Sat": "lör",
}
var longMonthNamesSvSE = map[string]string{
"January": "januari",
"February": "februari",
"March": "mars",
"April": "april",
"May": "maj",
"June": "juni",
"July": "juli",
"August": "augusti",
"September": "september",
"October": "oktober",
"November": "november",
"December": "december",
}
var shortMonthNamesSvSE = map[string]string{
"Jan": "jan",
"Feb": "feb",
"Mar": "mar",
"Apr": "apr",
"May": "maj",
"Jun": "jun",
"Jul": "jul",
"Aug": "aug",
"Sep": "sep",
"Oct": "okt",
"Nov": "nov",
"Dec": "dec",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "tr_TR" locale: Turkish (Turkey)
// ============================================================
var longDayNamesTrTR = map[string]string{
"Sunday": "Pazar",
"Monday": "Pazartesi",
"Tuesday": "Salı",
"Wednesday": "Çarşamba",
"Thursday": "Perşembe",
"Friday": "Cuma",
"Saturday": "Cumartesi",
}
var shortDayNamesTrTR = map[string]string{
"Sun": "Paz",
"Mon": "Pzt",
"Tue": "Sal",
"Wed": "Çar",
"Thu": "Per",
"Fri": "Cum",
"Sat": "Cmt",
}
var longMonthNamesTrTR = map[string]string{
"January": "Ocak",
"February": "Şubat",
"March": "Mart",
"April": "Nisan",
"May": "Mayıs",
"June": "Haziran",
"July": "Temmuz",
"August": "Ağustos",
"September": "Eylül",
"October": "Ekim",
"November": "Kasım",
"December": "Aralık",
}
var shortMonthNamesTrTR = map[string]string{
"Jan": "Oca",
"Feb": "Şub",
"Mar": "Mar",
"Apr": "Nis",
"May": "May",
"Jun": "Haz",
"Jul": "Tem",
"Aug": "Ağu",
"Sep": "Eyl",
"Oct": "Eki",
"Nov": "Kas",
"Dec": "Ara",
}

@ -0,0 +1,85 @@
package monday
// ============================================================
// Format rules for "uk_UA" locale: Ukrainian (Ukraine)
// ============================================================
var longDayNamesUkUA = map[string]string{
"Sunday": "Неділя",
"Monday": "Понеділок",
"Tuesday": "Вівторок",
"Wednesday": "Середа",
"Thursday": "Четвер",
"Friday": "П’ятниця",
"Saturday": "Субота",
}
var shortDayNamesUkUA = map[string]string{
"Sun": "Нд",
"Mon": "Пн",
"Tue": "Вт",
"Wed": "Ср",
"Thu": "Чт",
"Fri": "Пт",
"Sat": "Сб",
}
var longMonthNamesUkUA = map[string]string{
"January": "Січень",
"February": "Лютий",
"March": "Березень",
"April": "Квітень",
"May": "Травень",
"June": "Червень",
"July": "Липень",
"August": "Серпень",
"September": "Вересень",
"October": "Жовтень",
"November": "Листопад",
"December": "Грудень",
}
var longMonthNamesGenitiveUkUA = map[string]string{
"January": "січеня",
"February": "лютого",
"March": "березня",
"April": "квітня",
"May": "травня",
"June": "червня",
"July": "липня",
"August": "серпня",
"September": "вересня",
"October": "жовтня",
"November": "листопада",
"December": "грудня",
}
var shortMonthNamesUkUA = map[string]string{
"Jan": "Січ",
"Feb": "Лют",
"Mar": "Бер",
"Apr": "Кві",
"May": "Тра",
"Jun": "Чер",
"Jul": "Лип",
"Aug": "Сер",
"Sep": "Вер",
"Oct": "Жов",
"Nov": "Лис",
"Dec": "Гру",
}
var shortMonthNamesGenitiveUkUA = map[string]string{
"Jan": "січ",
"Feb": "лют",
"Mar": "бер",
"Apr": "кві",
"May": "тра",
"Jun": "чер",
"Jul": "лип",
"Aug": "сер",
"Sep": "вер",
"Oct": "жов",
"Nov": "лис",
"Dec": "гру",
}

@ -0,0 +1,75 @@
package monday
import "strings"
// ============================================================
// Format rules for "zh_CN" locale: Chinese (Mainland)
// ============================================================
var longDayNamesZhCN = map[string]string{
"Sunday": "星期日",
"Monday": "星期一",
"Tuesday": "星期二",
"Wednesday": "星期三",
"Thursday": "星期四",
"Friday": "星期五",
"Saturday": "星期六",
}
var shortDayNamesZhCN = map[string]string{
"Sun": "日",
"Mon": "一",
"Tue": "二",
"Wed": "三",
"Thu": "四",
"Fri": "五",
"Sat": "六",
}
var longMonthNamesZhCN = map[string]string{
"January": "1 月",
"February": "2 月",
"March": "3 月",
"April": "4 月",
"May": "5 月",
"June": "6 月",
"July": "7 月",
"August": "8 月",
"September": "9 月",
"October": "10 月",
"November": "11 月",
"December": "12 月",
}
var shortMonthNamesZhCN = map[string]string{
"Jan": "1",
"Feb": "2",
"Mar": "3",
"Apr": "4",
"May": "5",
"Jun": "6",
"Jul": "7",
"Aug": "8",
"Sep": "9",
"Oct": "10",
"Nov": "11",
"Dec": "12",
}
func parseFuncZhCommon(locale Locale) internalParseFunc {
return func(layout, value string) string {
// This special case is needed because Zh_CN/Zh/HK/... contains month names
// that consist of a number, a delimiter, and '月'. Example: "October" = "10 月"
//
// This means that probably default time package layout IDs like 'January' or 'Jan'
// shouldn't be used in Zh_*. But this is a time-compatible package, so someone
// might actually use those and we need to replace those before doing standard procedures.
for k, v := range knownMonthsLongReverse[locale] {
value = strings.Replace(value, k, v, -1)
}
return commonFormatFunc(value, layout,
knownDaysShortReverse[locale], knownDaysLongReverse[locale],
knownMonthsShortReverse[locale], knownMonthsLongReverse[locale], knownPeriods[locale])
}
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "zh_HK" locale: Chinese (Hong Kong)
// ============================================================
var longDayNamesZhHK = map[string]string{
"Sunday": "星期日",
"Monday": "星期一",
"Tuesday": "星期二",
"Wednesday": "星期三",
"Thursday": "星期四",
"Friday": "星期五",
"Saturday": "星期六",
}
var shortDayNamesZhHK = map[string]string{
"Sun": "日",
"Mon": "一",
"Tue": "二",
"Wed": "三",
"Thu": "四",
"Fri": "五",
"Sat": "六",
}
var longMonthNamesZhHK = map[string]string{
"January": "1 月",
"February": "2 月",
"March": "3 月",
"April": "4 月",
"May": "5 月",
"June": "6 月",
"July": "7 月",
"August": "8 月",
"September": "9 月",
"October": "10 月",
"November": "11 月",
"December": "12 月",
}
var shortMonthNamesZhHK = map[string]string{
"Jan": "1",
"Feb": "2",
"Mar": "3",
"Apr": "4",
"May": "5",
"Jun": "6",
"Jul": "7",
"Aug": "8",
"Sep": "9",
"Oct": "10",
"Nov": "11",
"Dec": "12",
}

@ -0,0 +1,55 @@
package monday
// ============================================================
// Format rules for "zh_TW" locale: Chinese (Taiwan)
// ============================================================
var longDayNamesZhTW = map[string]string{
"Sunday": "星期日",
"Monday": "星期一",
"Tuesday": "星期二",
"Wednesday": "星期三",
"Thursday": "星期四",
"Friday": "星期五",
"Saturday": "星期六",
}
var shortDayNamesZhTW = map[string]string{
"Sun": "日",
"Mon": "一",
"Tue": "二",
"Wed": "三",
"Thu": "四",
"Fri": "五",
"Sat": "六",
}
var longMonthNamesZhTW = map[string]string{
"January": "1 月",
"February": "2 月",
"March": "3 月",
"April": "4 月",
"May": "5 月",
"June": "6 月",
"July": "7 月",
"August": "8 月",
"September": "9 月",
"October": "10 月",
"November": "11 月",
"December": "12 月",
}
var shortMonthNamesZhTW = map[string]string{
"Jan": "1",
"Feb": "2",
"Mar": "3",
"Apr": "4",
"May": "5",
"Jun": "6",
"Jul": "7",
"Aug": "8",
"Sep": "9",
"Oct": "10",
"Nov": "11",
"Dec": "12",
}

@ -0,0 +1,91 @@
package monday
// Locale identifies locales supported by 'monday' package.
// Monday uses ICU locale identifiers. See http://userguide.icu-project.org/locale
type Locale string
// Locale constants represent all locales that are currently supported by
// this package.
const (
LocaleEnUS = "en_US" // English (United States)
LocaleEnGB = "en_GB" // English (United Kingdom)
LocaleDaDK = "da_DK" // Danish (Denmark)
LocaleNlBE = "nl_BE" // Dutch (Belgium)
LocaleNlNL = "nl_NL" // Dutch (Netherlands)
LocaleFiFI = "fi_FI" // Finnish (Finland)
LocaleFrFR = "fr_FR" // French (France)
LocaleFrCA = "fr_CA" // French (Canada)
LocaleDeDE = "de_DE" // German (Germany)
LocaleHuHU = "hu_HU" // Hungarian (Hungary)
LocaleItIT = "it_IT" // Italian (Italy)
LocaleNnNO = "nn_NO" // Norwegian Nynorsk (Norway)
LocaleNbNO = "nb_NO" // Norwegian Bokmål (Norway)
LocalePlPL = "pl_PL" // Polish (Poland)
LocalePtPT = "pt_PT" // Portuguese (Portugal)
LocalePtBR = "pt_BR" // Portuguese (Brazil)
LocaleRoRO = "ro_RO" // Romanian (Romania)
LocaleRuRU = "ru_RU" // Russian (Russia)
LocaleEsES = "es_ES" // Spanish (Spain)
LocaleCaES = "ca_ES" // Catalan (Spain)
LocaleSvSE = "sv_SE" // Swedish (Sweden)
LocaleTrTR = "tr_TR" // Turkish (Turkey)
LocaleUkUA = "uk_UA" // Ukrainian (Ukraine)
LocaleBgBG = "bg_BG" // Bulgarian (Bulgaria)
LocaleZhCN = "zh_CN" // Chinese (Mainland)
LocaleZhTW = "zh_TW" // Chinese (Taiwan)
LocaleZhHK = "zh_HK" // Chinese (Hong Kong)
LocaleKoKR = "ko_KR" // Korean (Korea)
LocaleJaJP = "ja_JP" // Japanese (Japan)
LocaleElGR = "el_GR" // Greek (Greece)
LocaleIdID = "id_ID" // Indonesian (Indonesia)
LocaleFrGP = "fr_GP" // French (Guadeloupe)
LocaleFrLU = "fr_LU" // French (Luxembourg)
LocaleFrMQ = "fr_MQ" // French (Martinique)
LocaleFrRE = "fr_RE" // French (Reunion)
LocaleFrGF = "fr_GF" // French (French Guiana)
LocaleCsCZ = "cs_CZ" // Czech (Czech Republic)
LocaleSlSI = "sl_SI" // Slovenian (Slovenia)
)
// ListLocales returns all locales supported by the package.
func ListLocales() []Locale {
return []Locale{
LocaleEnUS,
LocaleEnGB,
LocaleDaDK,
LocaleNlBE,
LocaleNlNL,
LocaleFiFI,
LocaleFrFR,
LocaleFrCA,
LocaleDeDE,
LocaleHuHU,
LocaleItIT,
LocaleNnNO,
LocaleNbNO,
LocalePlPL,
LocalePtPT,
LocalePtBR,
LocaleRoRO,
LocaleRuRU,
LocaleEsES,
LocaleCaES,
LocaleSvSE,
LocaleTrTR,
LocaleUkUA,
LocaleBgBG,
LocaleZhCN,
LocaleZhTW,
LocaleZhHK,
LocaleKoKR,
LocaleJaJP,
LocaleElGR,
LocaleFrGP,
LocaleFrLU,
LocaleFrMQ,
LocaleFrRE,
LocaleFrGF,
LocaleCsCZ,
LocaleSlSI,
}
}

@ -0,0 +1,563 @@
package monday
import (
"fmt"
"time"
)
// internalFormatFunc is a preprocessor for default time.Format func
type internalFormatFunc func(value, layout string) string
var internalFormatFuncs = map[Locale]internalFormatFunc{
LocaleEnUS: createCommonFormatFunc(LocaleEnUS),
LocaleEnGB: createCommonFormatFunc(LocaleEnGB),
LocaleDaDK: createCommonFormatFunc(LocaleDaDK),
LocaleNlBE: createCommonFormatFunc(LocaleNlBE),
LocaleNlNL: createCommonFormatFunc(LocaleNlNL),
LocaleFrFR: createCommonFormatFunc(LocaleFrFR),
LocaleFrCA: createCommonFormatFunc(LocaleFrFR),
LocaleFrGP: createCommonFormatFunc(LocaleFrFR),
LocaleFrLU: createCommonFormatFunc(LocaleFrFR),
LocaleFrMQ: createCommonFormatFunc(LocaleFrFR),
LocaleFrGF: createCommonFormatFunc(LocaleFrFR),
LocaleFrRE: createCommonFormatFunc(LocaleFrFR),
LocaleRuRU: createCommonFormatFuncWithGenitive(LocaleRuRU),
LocaleFiFI: createCommonFormatFuncWithGenitive(LocaleFiFI),
LocaleDeDE: createCommonFormatFunc(LocaleDeDE),
LocaleHuHU: createCommonFormatFunc(LocaleHuHU),
LocaleItIT: createCommonFormatFunc(LocaleItIT),
LocaleNnNO: createCommonFormatFunc(LocaleNnNO),
LocaleNbNO: createCommonFormatFunc(LocaleNbNO),
LocalePlPL: createCommonFormatFunc(LocalePlPL),
LocalePtPT: createCommonFormatFunc(LocalePtPT),
LocalePtBR: createCommonFormatFunc(LocalePtBR),
LocaleRoRO: createCommonFormatFunc(LocaleRoRO),
LocaleEsES: createCommonFormatFunc(LocaleEsES),
LocaleCaES: createCommonFormatFunc(LocaleCaES),
LocaleSvSE: createCommonFormatFunc(LocaleSvSE),
LocaleTrTR: createCommonFormatFunc(LocaleTrTR),
LocaleUkUA: createCommonFormatFuncWithGenitive(LocaleUkUA),
LocaleBgBG: createCommonFormatFunc(LocaleBgBG),
LocaleZhCN: createCommonFormatFunc(LocaleZhCN),
LocaleZhTW: createCommonFormatFunc(LocaleZhTW),
LocaleZhHK: createCommonFormatFunc(LocaleZhHK),
LocaleKoKR: createCommonFormatFunc(LocaleKoKR),
LocaleJaJP: createCommonFormatFunc(LocaleJaJP),
LocaleElGR: createCommonFormatFuncWithGenitive(LocaleElGR),
LocaleIdID: createCommonFormatFunc(LocaleIdID),
LocaleCsCZ: createCommonFormatFunc(LocaleCsCZ),
LocaleSlSI: createCommonFormatFunc(LocaleSlSI),
}
// internalParseFunc is a preprocessor for default time.ParseInLocation func
type internalParseFunc func(layout, value string) string
var internalParseFuncs = map[Locale]internalParseFunc{
LocaleEnUS: createCommonParseFunc(LocaleEnUS),
LocaleEnGB: createCommonParseFunc(LocaleEnGB),
LocaleDaDK: createCommonParseFunc(LocaleDaDK),
LocaleNlBE: createCommonParseFunc(LocaleNlBE),
LocaleNlNL: createCommonParseFunc(LocaleNlNL),
LocaleFrFR: createCommonParseFunc(LocaleFrFR),
LocaleFrCA: createCommonParseFunc(LocaleFrFR),
LocaleFrGP: createCommonParseFunc(LocaleFrFR),
LocaleFrLU: createCommonParseFunc(LocaleFrFR),
LocaleFrMQ: createCommonParseFunc(LocaleFrFR),
LocaleFrGF: createCommonParseFunc(LocaleFrFR),
LocaleFrRE: createCommonParseFunc(LocaleFrFR),
LocaleRuRU: createCommonParsetFuncWithGenitive(LocaleRuRU),
LocaleFiFI: createCommonParsetFuncWithGenitive(LocaleFiFI),
LocaleDeDE: createCommonParseFunc(LocaleDeDE),
LocaleHuHU: createCommonParseFunc(LocaleHuHU),
LocaleItIT: createCommonParseFunc(LocaleItIT),
LocaleNnNO: createCommonParseFunc(LocaleNnNO),
LocaleNbNO: createCommonParseFunc(LocaleNbNO),
LocalePlPL: parseFuncPtCommon(LocalePlPL),
LocalePtPT: parseFuncPtCommon(LocalePtPT),
LocalePtBR: parseFuncPtCommon(LocalePtBR),
LocaleRoRO: createCommonParseFunc(LocaleRoRO),
LocaleEsES: createCommonParseFunc(LocaleEsES),
LocaleCaES: createCommonParseFunc(LocaleCaES),
LocaleSvSE: createCommonParseFunc(LocaleSvSE),
LocaleTrTR: createCommonParseFunc(LocaleTrTR),
LocaleUkUA: createCommonParsetFuncWithGenitive(LocaleUkUA),
LocaleBgBG: createCommonParseFunc(LocaleBgBG),
LocaleZhCN: parseFuncZhCommon(LocaleZhCN),
LocaleZhTW: parseFuncZhCommon(LocaleZhTW),
LocaleZhHK: parseFuncZhCommon(LocaleZhHK),
LocaleKoKR: parseFuncKoCommon(LocaleKoKR),
LocaleJaJP: parseFuncJaCommon(LocaleJaJP),
LocaleElGR: createCommonParsetFuncWithGenitive(LocaleElGR),
LocaleIdID: createCommonParseFunc(LocaleIdID),
LocaleCsCZ: createCommonParseFunc(LocaleCsCZ),
LocaleSlSI: createCommonParseFunc(LocaleSlSI),
}
var knownDaysShort = map[Locale]map[string]string{} // Mapping for 'Format', days of week, short form
var knownDaysLong = map[Locale]map[string]string{} // Mapping for 'Format', days of week, long form
var knownMonthsLong = map[Locale]map[string]string{} // Mapping for 'Format', months: long form
var knownMonthsShort = map[Locale]map[string]string{} // Mapping for 'Format', months: short form
var knownMonthsGenitiveShort = map[Locale]map[string]string{} // Mapping for 'Format', special for names in genitive, short form
var knownMonthsGenitiveLong = map[Locale]map[string]string{} // Mapping for 'Format', special for names in genitive, long form
var knownPeriods = map[Locale]map[string]string{} // Mapping for 'Format', AM/PM
// Reverse maps for the same
var knownDaysShortReverse = map[Locale]map[string]string{} // Mapping for 'Format', days of week, short form
var knownDaysLongReverse = map[Locale]map[string]string{} // Mapping for 'Format', days of week, long form
var knownMonthsLongReverse = map[Locale]map[string]string{} // Mapping for 'Format', months: long form
var knownMonthsShortReverse = map[Locale]map[string]string{} // Mapping for 'Format', months: short form
var knownMonthsGenitiveShortReverse = map[Locale]map[string]string{} // Mapping for 'Format', special for names in genitive, short form
var knownMonthsGenitiveLongReverse = map[Locale]map[string]string{} // Mapping for 'Format', special for names in genitive, long form
var knownPeriodsReverse = map[Locale]map[string]string{} // Mapping for 'Format', AM/PM
func init() {
fillKnownWords()
}
func fillKnownWords() {
// En_US: English (United States)
fillKnownDaysLong(longDayNamesEnUS, LocaleEnUS)
fillKnownDaysShort(shortDayNamesEnUS, LocaleEnUS)
fillKnownMonthsLong(longMonthNamesEnUS, LocaleEnUS)
fillKnownMonthsShort(shortMonthNamesEnUS, LocaleEnUS)
// En_GB: English (United Kingdom)
fillKnownDaysLong(longDayNamesEnUS, LocaleEnGB)
fillKnownDaysShort(shortDayNamesEnUS, LocaleEnGB)
fillKnownMonthsLong(longMonthNamesEnUS, LocaleEnGB)
fillKnownMonthsShort(shortMonthNamesEnUS, LocaleEnGB)
// Da_DK: Danish (Denmark)
fillKnownDaysLong(longDayNamesDaDK, LocaleDaDK)
fillKnownDaysShort(shortDayNamesDaDK, LocaleDaDK)
fillKnownMonthsLong(longMonthNamesDaDK, LocaleDaDK)
fillKnownMonthsShort(shortMonthNamesDaDK, LocaleDaDK)
// Nl_BE: Dutch (Belgium)
fillKnownDaysLong(longDayNamesNlBE, LocaleNlBE)
fillKnownDaysShort(shortDayNamesNlBE, LocaleNlBE)
fillKnownMonthsLong(longMonthNamesNlBE, LocaleNlBE)
fillKnownMonthsShort(shortMonthNamesNlBE, LocaleNlBE)
// Nl_NL: Dutch (Netherlands)
fillKnownDaysLong(longDayNamesNlBE, LocaleNlNL)
fillKnownDaysShort(shortDayNamesNlBE, LocaleNlNL)
fillKnownMonthsLong(longMonthNamesNlBE, LocaleNlNL)
fillKnownMonthsShort(shortMonthNamesNlBE, LocaleNlNL)
// Fi_FI: Finnish (Finland)
fillKnownDaysLong(longDayNamesFiFI, LocaleFiFI)
fillKnownDaysShort(shortDayNamesFiFI, LocaleFiFI)
fillKnownMonthsLong(longMonthNamesFiFI, LocaleFiFI)
fillKnownMonthsShort(shortMonthNamesFiFI, LocaleFiFI)
fillKnownMonthsGenitiveLong(longMonthNamesGenitiveFiFI, LocaleFiFI)
fillKnownMonthsGenitiveShort(shortMonthNamesFiFI, LocaleFiFI)
// Fr_FR: French (France)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrFR)
fillKnownDaysShort(shortDayNamesFrFR, LocaleFrFR)
fillKnownMonthsLong(longMonthNamesFrFR, LocaleFrFR)
fillKnownMonthsShort(shortMonthNamesFrFR, LocaleFrFR)
// Fr_CA: French (Canada)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrCA)
fillKnownDaysShort(shortDayNamesFrFR, LocaleFrCA)
fillKnownMonthsLong(longMonthNamesFrFR, LocaleFrCA)
fillKnownMonthsShort(shortMonthNamesFrFR, LocaleFrCA)
// Fr_GP: French (Guadeloupe)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrGP)
fillKnownDaysShort(shortDayNamesFrFR, LocaleFrGP)
fillKnownMonthsLong(longMonthNamesFrFR, LocaleFrGP)
fillKnownMonthsShort(shortMonthNamesFrFR, LocaleFrGP)
// Fr_LU: French (Luxembourg)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrLU)
fillKnownDaysShort(longDayNamesFrFR, LocaleFrLU)
fillKnownMonthsLong(longDayNamesFrFR, LocaleFrLU)
fillKnownMonthsShort(longDayNamesFrFR, LocaleFrLU)
// Fr_MQ: French (Martinique)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrMQ)
fillKnownDaysShort(longDayNamesFrFR, LocaleFrMQ)
fillKnownMonthsLong(longDayNamesFrFR, LocaleFrMQ)
fillKnownMonthsShort(longDayNamesFrFR, LocaleFrMQ)
// Fr_GF: French (French Guiana)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrGF)
fillKnownDaysShort(longDayNamesFrFR, LocaleFrGF)
fillKnownMonthsLong(longDayNamesFrFR, LocaleFrGF)
fillKnownMonthsShort(longDayNamesFrFR, LocaleFrGF)
// Fr_RE: French (French Reunion)
fillKnownDaysLong(longDayNamesFrFR, LocaleFrRE)
fillKnownDaysShort(longDayNamesFrFR, LocaleFrRE)
fillKnownMonthsLong(longDayNamesFrFR, LocaleFrRE)
fillKnownMonthsShort(longDayNamesFrFR, LocaleFrRE)
// De_DE: German (Germany)
fillKnownDaysLong(longDayNamesDeDE, LocaleDeDE)
fillKnownDaysShort(shortDayNamesDeDE, LocaleDeDE)
fillKnownMonthsLong(longMonthNamesDeDE, LocaleDeDE)
fillKnownMonthsShort(shortMonthNamesDeDE, LocaleDeDE)
// Hu_HU: Hungarian (Hungary)
fillKnownDaysLong(longDayNamesHuHU, LocaleHuHU)
fillKnownDaysShort(shortDayNamesHuHU, LocaleHuHU)
fillKnownMonthsLong(longMonthNamesHuHU, LocaleHuHU)
fillKnownMonthsShort(shortMonthNamesHuHU, LocaleHuHU)
// It_IT: Italian (Italy)
fillKnownDaysLong(longDayNamesItIT, LocaleItIT)
fillKnownDaysShort(shortDayNamesItIT, LocaleItIT)
fillKnownMonthsLong(longMonthNamesItIT, LocaleItIT)
fillKnownMonthsShort(shortMonthNamesItIT, LocaleItIT)
// Nn_NO: Norwegian Nynorsk (Norway)
fillKnownDaysLong(longDayNamesNnNO, LocaleNnNO)
fillKnownDaysShort(shortDayNamesNnNO, LocaleNnNO)
fillKnownMonthsLong(longMonthNamesNnNO, LocaleNnNO)
fillKnownMonthsShort(shortMonthNamesNnNO, LocaleNnNO)
// Nb_NO: Norwegian Bokmål (Norway)
fillKnownDaysLong(longDayNamesNbNO, LocaleNbNO)
fillKnownDaysShort(shortDayNamesNbNO, LocaleNbNO)
fillKnownMonthsLong(longMonthNamesNbNO, LocaleNbNO)
fillKnownMonthsShort(shortMonthNamesNbNO, LocaleNbNO)
// Pl_PL: Polish (Poland)
fillKnownDaysLong(longDayNamesPlPL, LocalePlPL)
fillKnownDaysShort(shortDayNamesPlPL, LocalePlPL)
fillKnownMonthsLong(longMonthNamesPlPL, LocalePlPL)
fillKnownMonthsShort(shortMonthNamesPlPL, LocalePlPL)
// Pt_PT: Portuguese (Portugal)
fillKnownDaysLong(longDayNamesPtPT, LocalePtPT)
fillKnownDaysShort(shortDayNamesPtPT, LocalePtPT)
fillKnownMonthsLong(longMonthNamesPtPT, LocalePtPT)
fillKnownMonthsShort(shortMonthNamesPtPT, LocalePtPT)
// Pt_BR: Portuguese (Brazil)
fillKnownDaysLong(longDayNamesPtBR, LocalePtBR)
fillKnownDaysShort(shortDayNamesPtBR, LocalePtBR)
fillKnownMonthsLong(longMonthNamesPtBR, LocalePtBR)
fillKnownMonthsShort(shortMonthNamesPtBR, LocalePtBR)
// Ro_RO: Romanian (Romania)
fillKnownDaysLong(longDayNamesRoRO, LocaleRoRO)
fillKnownDaysShort(shortDayNamesRoRO, LocaleRoRO)
fillKnownMonthsLong(longMonthNamesRoRO, LocaleRoRO)
fillKnownMonthsShort(shortMonthNamesRoRO, LocaleRoRO)
// Ru_RU: Russian (Russia)
fillKnownDaysLong(longDayNamesRuRU, LocaleRuRU)
fillKnownDaysShort(shortDayNamesRuRU, LocaleRuRU)
fillKnownMonthsLong(longMonthNamesRuRU, LocaleRuRU)
fillKnownMonthsShort(shortMonthNamesRuRU, LocaleRuRU)
fillKnownMonthsGenitiveLong(longMonthNamesGenitiveRuRU, LocaleRuRU)
fillKnownMonthsGenitiveShort(shortMonthNamesGenitiveRuRU, LocaleRuRU)
// Es_ES: Spanish (Spain)
fillKnownDaysLong(longDayNamesEsES, LocaleEsES)
fillKnownDaysShort(shortDayNamesEsES, LocaleEsES)
fillKnownMonthsLong(longMonthNamesEsES, LocaleEsES)
fillKnownMonthsShort(shortMonthNamesEsES, LocaleEsES)
// Ca_ES: Catalan (Spain)
fillKnownDaysLong(longDayNamesCaES, LocaleCaES)
fillKnownDaysShort(shortDayNamesCaES, LocaleCaES)
fillKnownMonthsLong(longMonthNamesCaES, LocaleCaES)
fillKnownMonthsShort(shortMonthNamesCaES, LocaleCaES)
// Sv_SE: Swedish (Sweden)
fillKnownDaysLong(longDayNamesSvSE, LocaleSvSE)
fillKnownDaysShort(shortDayNamesSvSE, LocaleSvSE)
fillKnownMonthsLong(longMonthNamesSvSE, LocaleSvSE)
fillKnownMonthsShort(shortMonthNamesSvSE, LocaleSvSE)
// Tr_TR: Turkish (Turkey)
fillKnownDaysLong(longDayNamesTrTR, LocaleTrTR)
fillKnownDaysShort(shortDayNamesTrTR, LocaleTrTR)
fillKnownMonthsLong(longMonthNamesTrTR, LocaleTrTR)
fillKnownMonthsShort(shortMonthNamesTrTR, LocaleTrTR)
// Uk_UA: Ukrainian (Ukraine)
fillKnownDaysLong(longDayNamesUkUA, LocaleUkUA)
fillKnownDaysShort(shortDayNamesUkUA, LocaleUkUA)
fillKnownMonthsLong(longMonthNamesUkUA, LocaleUkUA)
fillKnownMonthsShort(shortMonthNamesUkUA, LocaleUkUA)
fillKnownMonthsGenitiveLong(longMonthNamesGenitiveUkUA, LocaleUkUA)
fillKnownMonthsGenitiveShort(shortMonthNamesGenitiveUkUA, LocaleUkUA)
// Bg_BG: Bulgarian (Bulgaria)
fillKnownDaysLong(longDayNamesBgBG, LocaleBgBG)
fillKnownDaysShort(shortDayNamesBgBG, LocaleBgBG)
fillKnownMonthsLong(longMonthNamesBgBG, LocaleBgBG)
fillKnownMonthsShort(shortMonthNamesBgBG, LocaleBgBG)
// Zh_CN: Chinese (Mainland)
fillKnownDaysLong(longDayNamesZhCN, LocaleZhCN)
fillKnownDaysShort(shortDayNamesZhCN, LocaleZhCN)
fillKnownMonthsLong(longMonthNamesZhCN, LocaleZhCN)
fillKnownMonthsShort(shortMonthNamesZhCN, LocaleZhCN)
// Zh_TW: Chinese (Taiwan)
fillKnownDaysLong(longDayNamesZhTW, LocaleZhTW)
fillKnownDaysShort(shortDayNamesZhTW, LocaleZhTW)
fillKnownMonthsLong(longMonthNamesZhTW, LocaleZhTW)
fillKnownMonthsShort(shortMonthNamesZhTW, LocaleZhTW)
// Zh_HK: Chinese (Hong Kong)
fillKnownDaysLong(longDayNamesZhHK, LocaleZhHK)
fillKnownDaysShort(shortDayNamesZhHK, LocaleZhHK)
fillKnownMonthsLong(longMonthNamesZhHK, LocaleZhHK)
fillKnownMonthsShort(shortMonthNamesZhHK, LocaleZhHK)
// Ko_KR: Korean (Korea)
fillKnownDaysLong(longDayNamesKoKR, LocaleKoKR)
fillKnownDaysShort(shortDayNamesKoKR, LocaleKoKR)
fillKnownMonthsLong(longMonthNamesKoKR, LocaleKoKR)
fillKnownMonthsShort(shortMonthNamesKoKR, LocaleKoKR)
fillKnownPeriods(periodsKoKR, LocaleKoKR)
// Ja_JP: Japanese (Japan)
fillKnownDaysLong(longDayNamesJaJP, LocaleJaJP)
fillKnownDaysShort(shortDayNamesJaJP, LocaleJaJP)
fillKnownMonthsLong(longMonthNamesJaJP, LocaleJaJP)
fillKnownMonthsShort(shortMonthNamesJaJP, LocaleJaJP)
fillKnownPeriods(periodsJaJP, LocaleJaJP)
// El_GR: Greek (Greece)
fillKnownDaysLong(longDayNamesElGR, LocaleElGR)
fillKnownDaysShort(shortDayNamesElGR, LocaleElGR)
fillKnownMonthsLong(longMonthNamesElGR, LocaleElGR)
fillKnownMonthsShort(shortMonthNamesElGR, LocaleElGR)
fillKnownMonthsGenitiveLong(longMonthNamesGenitiveElGR, LocaleElGR)
fillKnownMonthsGenitiveShort(shortMonthNamesElGR, LocaleElGR)
fillKnownPeriods(periodsElGR, LocaleElGR)
// Id_ID: Indonesia (Indonesia)
fillKnownDaysLong(longDayNamesIdID, LocaleIdID)
fillKnownDaysShort(shortDayNamesIdID, LocaleIdID)
fillKnownMonthsLong(longMonthNamesIdID, LocaleIdID)
fillKnownMonthsShort(shortMonthNamesIdID, LocaleIdID)
// Cs_CZ: Czech (Czech Republic)
fillKnownDaysLong(longDayNamesCsCZ, LocaleCsCZ)
fillKnownDaysShort(shortDayNamesCsCZ, LocaleCsCZ)
fillKnownMonthsLong(longMonthNamesCsCZ, LocaleCsCZ)
fillKnownMonthsShort(shortMonthNamesCsCZ, LocaleCsCZ)
// Sl_SI: Slovenian (Slovenia)
fillKnownDaysLong(longDayNamesSlSI, LocaleSlSI)
fillKnownDaysShort(shortDayNamesSlSI, LocaleSlSI)
fillKnownMonthsLong(longMonthNamesSlSI, LocaleSlSI)
fillKnownMonthsShort(shortMonthNamesSlSI, LocaleSlSI)
}
func fill(src map[string]string, dest map[Locale]map[string]string, locale Locale) {
loc, ok := dest[locale]
if !ok {
loc = make(map[string]string, len(src))
dest[locale] = loc
}
for k, v := range src {
loc[k] = v
}
}
func fillReverse(src map[string]string, dest map[Locale]map[string]string, locale Locale) {
loc, ok := dest[locale]
if !ok {
loc = make(map[string]string, len(src))
dest[locale] = loc
}
for k, v := range src {
loc[v] = k
}
}
func fillKnownMonthsGenitiveShort(src map[string]string, locale Locale) {
fillReverse(src, knownMonthsGenitiveShortReverse, locale)
fill(src, knownMonthsGenitiveShort, locale)
}
func fillKnownMonthsGenitiveLong(src map[string]string, locale Locale) {
fillReverse(src, knownMonthsGenitiveLongReverse, locale)
fill(src, knownMonthsGenitiveLong, locale)
}
func fillKnownDaysShort(src map[string]string, locale Locale) {
fillReverse(src, knownDaysShortReverse, locale)
fill(src, knownDaysShort, locale)
}
func fillKnownDaysLong(src map[string]string, locale Locale) {
fillReverse(src, knownDaysLongReverse, locale)
fill(src, knownDaysLong, locale)
}
func fillKnownMonthsShort(src map[string]string, locale Locale) {
fillReverse(src, knownMonthsShortReverse, locale)
fill(src, knownMonthsShort, locale)
}
func fillKnownMonthsLong(src map[string]string, locale Locale) {
fillReverse(src, knownMonthsLongReverse, locale)
fill(src, knownMonthsLong, locale)
}
func fillKnownPeriods(src map[string]string, locale Locale) {
fillReverse(src, knownPeriodsReverse, locale)
fill(src, knownPeriods, locale)
}
// Format is the standard time.Format wrapper, that replaces known standard 'time' package
// identifiers for months and days to their equivalents in the specified language.
//
// Values of variables 'longDayNames', 'shortDayNames', 'longMonthNames', 'shortMonthNames'
// from file 'time/format.go' (revision 'go1') are chosen as the 'known' words.
//
// Some languages have specific behavior, e.g. in Russian language
// month names have different suffix when they are presented stand-alone (i.e. in a list or something)
// and yet another one when they are part of a formatted date.
// So, even though March is "Март" in Russian, correctly formatted today's date would be: "7 марта 2007".
// Thus, some transformations for some languages may be a bit more complex than just plain replacements.
func Format(dt time.Time, layout string, locale Locale) string {
fm := dt.Format(layout)
intFunc, ok := internalFormatFuncs[locale]
if !ok {
return fm
}
return intFunc(fm, layout)
}
// ParseInLocation is the standard time.ParseInLocation wrapper, which replaces
// known month/day translations for a specified locale back to English before
// calling time.ParseInLocation. So, you can parse localized dates with this wrapper.
func ParseInLocation(layout, value string, loc *time.Location, locale Locale) (time.Time, error) {
intFunc, ok := internalParseFuncs[locale]
if ok {
value = intFunc(layout, value)
} else {
return time.Now(), fmt.Errorf("unsupported locale: %v", locale)
}
return time.ParseInLocation(layout, value, loc)
}
// Parse is the standard time.Parse wrapper, which replaces
// known month/day translations for a specified locale back to English before
// calling time.Parse.
func Parse(layout, value string, locale Locale) (time.Time, error) {
intFunc, ok := internalParseFuncs[locale]
if ok {
value = intFunc(layout, value)
} else {
return time.Now(), fmt.Errorf("unsupported locale: %v", locale)
}
return time.Parse(layout, value)
}
// GetShortDays retrieves the list of days for the given locale.
// "Short" days are abbreviated versions of the full day names. In English,
// for example, this might return "Tues" for "Tuesday". For certain locales,
// the long and short form of the days of the week may be the same.
//
// If the locale cannot be found, the resulting slice will be nil.
func GetShortDays(locale Locale) []string {
days, ok := knownDaysShort[locale]
if !ok {
return nil
}
var dayOrder []string
// according to https://www.timeanddate.com/calendar/days/monday.html
// only Canada, USA and Japan use Sunday as first day of the week
switch locale {
case LocaleEnUS, LocaleJaJP:
dayOrder = dayShortOrderSundayFirst
default:
dayOrder = dayShortOrderMondayFirst
}
ret := make([]string, 0, len(days))
for _, day := range dayOrder {
ret = append(ret, days[day])
}
return ret
}
// GetShortMonths retrieves the list of months for the given locale.
// "Short" months are abbreviated versions of the full month names. In
// English, for example, this might return "Jan" for "January". For
// certain locales, the long and short form of the months may be the same.
//
// If the locale cannot be found, the resulting slice will be nil.
func GetShortMonths(locale Locale) []string {
months, ok := knownMonthsShort[locale]
if !ok {
return nil
}
ret := make([]string, 0, len(months))
for _, m := range monthShortOrder {
ret = append(ret, months[m])
}
return ret
}
// GetLongDays retrieves the list of days for the given locale. It will return
// the full name of the days of the week.
//
// If the locale cannot be found, the resulting slice will be nil.
func GetLongDays(locale Locale) []string {
days, ok := knownDaysLong[locale]
if !ok {
return nil
}
var dayOrder []string
// according to https://www.timeanddate.com/calendar/days/monday.html
// only Canada, USA and Japan use Sunday as first day of the week
switch locale {
case LocaleEnUS, LocaleJaJP:
dayOrder = dayLongOrderSundayFirst
default:
dayOrder = dayLongOrderMondayFirst
}
ret := make([]string, 0, len(days))
for _, day := range dayOrder {
ret = append(ret, days[day])
}
return ret
}
// GetLongMonths retrieves the list of months for the given locale. In
// contrast to the "short" version of this function, this functions returns
// the full name of the month.
//
// If the locale cannot be found, the resulting slice will be nil.
func GetLongMonths(locale Locale) []string {
months, ok := knownMonthsLong[locale]
if !ok {
return nil
}
ret := make([]string, 0, len(months))
for _, m := range monthLongOrder {
ret = append(ret, months[m])
}
return ret
}

@ -0,0 +1,67 @@
package monday
import "sync"
var keyExists = struct{}{}
// Set is a thread safe set data structure.
//
// It is ported from https://github.com/fatih/set with only the required functionality.
type set struct {
m map[Locale]struct{}
l sync.RWMutex
}
// NewSet allocates and returns a new Set. It accepts a variable number of
// arguments to populate the initial set. If nothing is passed a Set with zero
// size is created.
func newSet(items ...Locale) *set {
s := set{
m: make(map[Locale]struct{}),
}
s.Add(items...)
return &s
}
// Add adds the specified items (one or more) to the set. The underlying
// Set s is modified. If no items are passed it silently returns.
func (s *set) Add(items ...Locale) {
if len(items) == 0 {
return
}
s.l.Lock()
defer s.l.Unlock()
for _, item := range items {
s.m[item] = keyExists
}
}
// Each traverses the items in the Set, calling the provided function f for
// each set member. Traversal will continue until all items in the Set have
// been visited, or if the closure returns false.
func (s *set) Each(f func(item Locale) bool) {
s.l.RLock()
defer s.l.RUnlock()
for item := range s.m {
if !f(item) {
break
}
}
}
// Has looks for the existence of items passed. It returns false if nothing is
// passed. For multiple items it returns true only if all of the items exist.
func (s *set) Has(items ...Locale) bool {
if len(items) == 0 {
return false
}
s.l.RLock()
defer s.l.RUnlock()
has := true
for _, item := range items {
if _, has = s.m[item]; !has {
break
}
}
return has
}

@ -0,0 +1,92 @@
package monday
import (
"strings"
"unicode"
"unicode/utf8"
)
// dateStringLayoutItem represents one word or set of delimiters between words.
// This is an abstraction level above date raw character string of date representation.
//
// Example: "1 February / 2013" ->
// dateStringLayoutItem { item: "1", isWord: true }
// dateStringLayoutItem { item: " ", isWord: false }
// dateStringLayoutItem { item: "February", isWord: true }
// dateStringLayoutItem { item: " / ", isWord: false }
// dateStringLayoutItem { item: "2013", isWord: true }
type dateStringLayoutItem struct {
item string
isWord bool // true if this is a sequence of letters/digits (as opposed to a sequence of non-letters like delimiters)
isDigit bool // true if this is a sequence only containing digits
}
// extractLetterSequence extracts first word (sequence of letters ending with a non-letter)
// starting with the specified index and wraps it to dateStringLayoutItem according to the type
// of the word.
func extractLetterSequence(originalStr string, index int) (it dateStringLayoutItem) {
letters := &strings.Builder{}
bytesToParse := []byte(originalStr[index:])
runeCount := utf8.RuneCount(bytesToParse)
var isWord bool
var isDigit bool
letters.Grow(runeCount)
for i := 0; i < runeCount; i++ {
rne, runeSize := utf8.DecodeRune(bytesToParse)
bytesToParse = bytesToParse[runeSize:]
if i == 0 {
isWord = unicode.IsLetter(rne)
isDigit = unicode.IsDigit(rne)
} else {
if (isWord && (!unicode.IsLetter(rne) && !unicode.IsDigit(rne))) ||
(isDigit && !unicode.IsDigit(rne)) ||
(!isWord && unicode.IsLetter(rne)) ||
(!isDigit && unicode.IsDigit(rne)) {
break
}
}
letters.WriteRune(rne)
}
it.item = letters.String()
it.isWord = isWord
it.isDigit = isDigit
return
}
// stringToLayoutItems transforms raw date string (like "2 Mar 2012") into
// a set of dateStringLayoutItems, which are more convenient to work with
// in other analysis modules.
func stringToLayoutItems(dateStr string) (seqs []dateStringLayoutItem) {
i := 0
for i < len(dateStr) {
seq := extractLetterSequence(dateStr, i)
i += len(seq.item)
seqs = append(seqs, seq)
}
return
}
func layoutToString(li []dateStringLayoutItem) string {
// This function is expensive enough to be worth counting
// bytes and allocating all in one go.
numChars := 0
for _, v := range li {
numChars += len(v.item)
}
sb := &strings.Builder{}
sb.Grow(numChars)
for _, v := range li {
sb.WriteString(v.item)
}
return sb.String()
}

@ -0,0 +1 @@
* text eol=lf

@ -0,0 +1,44 @@
### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool
*.out
# Fyne Cross build folder
fyne-cross/
### Linux ###
*~
### Vim ###
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history

@ -0,0 +1,40 @@
run:
timeout: 5m
linters:
disable-all: true
enable:
# Default
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# Manually enabled
- dupl
- gocyclo
- gofmt
- golint
- goprintffuncname
- lll
- misspell
- nakedret
- prealloc
- unconvert
- whitespace
linter-settings:
gocyclo:
# Minimal code complexity to report, 30 by default
min-complexity: 15
govet:
# Report about shadowed variables
check-shadowing: true
issues:
exclude-use-default: false
exclude:
# govet: Common false positives
- (possible misuse of unsafe.Pointer|should have signature)

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Alexis Jeandeau
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,135 @@
# go-locale
[![PkgGoDev](https://pkg.go.dev/badge/github.com/jeandeaual/go-locale)](https://pkg.go.dev/github.com/jeandeaual/go-locale)
[![Go Report Card](https://goreportcard.com/badge/github.com/jeandeaual/go-locale)](https://goreportcard.com/report/github.com/jeandeaual/go-locale)
[![Coverage Status](https://coveralls.io/repos/github/jeandeaual/go-locale/badge.svg?branch=master)](https://coveralls.io/github/jeandeaual/go-locale?branch=master)
[![test](https://github.com/jeandeaual/go-locale/workflows/test/badge.svg)](https://github.com/jeandeaual/go-locale/actions?query=workflow%3Atest)
Go library used to retrieve the current locale(s) of the operating system.
## OS Support
* Windows\
Using [`GetUserDefaultLocaleName`](https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename) and [`GetSystemDefaultLocaleName`](https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getsystemdefaultlocalename).
* macOS\
Using `defaults read -g AppleLocale` and `defaults read -g AppleLanguages` (since environment variables like `LANG` are not usually set on macOS).
* Unix-like systems (Linux, BSD, etc.)\
Using the `LANGUAGE`, `LC_ALL`, `LC_MESSAGES` and `LANG` environment variables.
* WASM (JavaScript)\
Using [`navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language) and [`navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages).
* iOS\
Using [`[NSLocale preferredLanguages]`](https://developer.apple.com/documentation/foundation/nslocale/1415614-preferredlanguages).
* Android\
Using [`getResources().getConfiguration().getLocales`](https://developer.android.com/reference/android/content/res/Configuration#getLocales()) for Android N or later, or [`getResources().getConfiguration().locale`](https://developer.android.com/reference/android/content/res/Configuration#locale) otherwise.
Note: for Android, you'll first need to call `SetRunOnJVM`, depending on which mobile framework you're using:
* For [Fyne](https://fyne.io/):
```go
import (
"github.com/fyne-io/mobile/app"
"github.com/jeandeaual/go-locale"
)
func init() {
locale.SetRunOnJVM(app.RunOnJVM)
}
```
* For [gomobile](https://github.com/golang/go/wiki/Mobile):
```go
import (
"golang.org/x/mobile/app"
"github.com/jeandeaual/go-locale"
)
func init() {
locale.SetRunOnJVM(app.RunOnJVM)
}
```
## Usage
## GetLocales
`GetLocales` returns the user's preferred locales, by order of preference, as a slice of [IETF BCP 47 language tag](https://tools.ietf.org/rfc/bcp/bcp47.txt) (e.g. `[]string{"en-US", "fr-FR", "ja-JP"}`).
This works if the user set multiple languages on macOS and other Unix systems.
Otherwise, it returns a slice with a single locale.
```go
userLocales, err := locale.GetLocales()
if err == nil {
fmt.Println("Locales:", userLocales)
}
```
This can be used with [golang.org/x/text](https://godoc.org/golang.org/x/text) or [go-i18n](https://github.com/nicksnyder/go-i18n) to set the localizer's language preferences:
```go
import (
"github.com/jeandeaual/go-locale"
"golang.org/x/text/message"
)
func main() {
userLocales, _ := locale.GetLocales()
p := message.NewPrinter(message.MatchLanguage(userLocales...))
...
}
```
```go
import (
"github.com/jeandeaual/go-locale"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
func main() {
userLocales, _ := locale.GetLocales()
bundle := i18n.NewBundle(language.English)
localizer := i18n.NewLocalizer(bundle, userLocales...)
...
}
```
For a complete example, see [here](examples/getlocale-gui/main.go).
## GetLocale
`GetLocale` returns the current locale as defined in [IETF BCP 47](https://tools.ietf.org/rfc/bcp/bcp47.txt) (e.g. `"en-US"`).
```go
userLocale, err := locale.GetLocale()
if err == nil {
fmt.Println("Locale:", userLocale)
}
```
## GetLanguage
`GetLanguage` returns the current language as an [ISO 639](http://en.wikipedia.org/wiki/ISO_639) language code (e.g. `"en"`).
```go
userLanguage, err := locale.GetLanguage()
if err == nil {
fmt.Println("Language:", userLocale)
}
```
## GetRegion
`GetRegion` returns the current language as an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) country code (e.g. `"US"`).
```go
userRegion, err := locale.GetRegion()
if err == nil {
fmt.Println("Region:", userRegion)
}
```
## Aknowledgements
Inspired by [jibber_jabber](https://github.com/cloudfoundry-attic/jibber_jabber).

@ -0,0 +1,243 @@
// +build android
#include <android/log.h>
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "GoLog", __VA_ARGS__)
static const char *jstringToCharCopy(JNIEnv *env, const jstring string)
{
const char *chars = (*env)->GetStringUTFChars(env, string, NULL);
const char *copy = strdup(chars);
(*env)->ReleaseStringUTFChars(env, string, chars);
return copy;
}
static jclass findClass(JNIEnv *env, const char *class_name)
{
jclass clazz = (*env)->FindClass(env, class_name);
if (clazz == NULL)
{
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return clazz;
}
static jmethodID findMethod(JNIEnv *env, jclass clazz, const char *name, const char *sig)
{
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == 0)
{
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static jfieldID findField(JNIEnv *env, jclass clazz, const char *name, const char *sig)
{
jfieldID f = (*env)->GetFieldID(env, clazz, name, sig);
if (f == 0)
{
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return f;
}
static jfieldID getStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig)
{
jfieldID f = (*env)->GetStaticFieldID(env, clazz, name, sig);
if (f == 0)
{
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find static field %s %s", name, sig);
return 0;
}
return f;
}
static const char *toLanguageTag(JNIEnv *env, jobject locale)
{
const jclass java_util_Locale = findClass(env, "java/util/Locale");
const jstring localeStr =
(*env)->CallObjectMethod(
env,
locale,
(*env)->GetMethodID(env, java_util_Locale, "toLanguageTag", "()Ljava/lang/String;"));
return jstringToCharCopy(env, localeStr);
}
static const char *toLanguageTags(JNIEnv *env, jobject locales, jclass android_os_LocaleList)
{
const jstring localeStr =
(*env)->CallObjectMethod(
env,
locales,
(*env)->GetMethodID(env, android_os_LocaleList, "toLanguageTags", "()Ljava/lang/String;"));
return jstringToCharCopy(env, localeStr);
}
static int getAPIVersion(JNIEnv *env)
{
// VERSION is a nested class within android.os.Build (hence "$" rather than "/")
const jclass versionClass = findClass(env, "android/os/Build$VERSION");
const jfieldID sdkIntFieldID = getStaticFieldID(env, versionClass, "SDK_INT", "I");
int sdkInt = (*env)->GetStaticIntField(env, versionClass, sdkIntFieldID);
return sdkInt;
}
static const jobject getConfiguration(JNIEnv *env, jobject context)
{
const jclass android_content_ContextWrapper = findClass(env, "android/content/ContextWrapper");
const jclass android_content_res_Resources = findClass(env, "android/content/res/Resources");
const jobject resources =
(*env)->CallObjectMethod(
env,
context,
findMethod(env, android_content_ContextWrapper, "getResources", "()Landroid/content/res/Resources;"));
const jobject configuration =
(*env)->CallObjectMethod(
env,
resources,
findMethod(env, android_content_res_Resources, "getConfiguration", "()Landroid/content/res/Configuration;"));
return configuration;
}
static const jobject getLocaleObject(JNIEnv *env, jobject context)
{
const jobject configuration = getConfiguration(env, context);
const jclass android_content_res_Configuration = findClass(env, "android/content/res/Configuration");
int version = getAPIVersion(env);
// Android N or later
// See https://developer.android.com/reference/android/content/res/Configuration#locale
if (version >= 24) {
const jclass android_os_LocaleList = findClass(env, "android/os/LocaleList");
const jobject locales =
(*env)->CallObjectMethod(
env,
configuration,
findMethod(env, android_content_res_Configuration, "getLocales", "()Landroid/os/LocaleList;"));
return (*env)->CallObjectMethod(
env,
locales,
findMethod(env, android_os_LocaleList, "get", "(I)Ljava/util/Locale;"),
0);
} else {
return (*env)->GetObjectField(
env,
configuration,
findField(env, android_content_res_Configuration, "locale", "Ljava/util/Locale;"));
}
}
// Basically the same as `getResources().getConfiguration().getLocales()` for Android N and later,
// or `getResources().getConfiguration().locale` for earlier Android version.
const char *getLocales(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx)
{
JavaVM *vm = (JavaVM *)java_vm;
JNIEnv *env = (JNIEnv *)jni_env;
jobject context = (jobject)ctx;
const jobject configuration = getConfiguration(env, context);
const jclass android_content_res_Configuration = findClass(env, "android/content/res/Configuration");
int version = getAPIVersion(env);
// Android N or later
// See https://developer.android.com/reference/android/content/res/Configuration#locale
if (version >= 24) {
const jclass android_os_LocaleList = findClass(env, "android/os/LocaleList");
const jobject locales =
(*env)->CallObjectMethod(
env,
configuration,
findMethod(env, android_content_res_Configuration, "getLocales", "()Landroid/os/LocaleList;"));
return toLanguageTags(env, locales, android_os_LocaleList);
} else {
const jobject locale =
(*env)->GetObjectField(
env,
configuration,
findField(env, android_content_res_Configuration, "locale", "Ljava/util/Locale;"));
return toLanguageTag(env, locale);
}
}
// Basically the same as `getResources().getConfiguration().getLocales().get(0).toString()` for Android N and later,
// or `getResources().getConfiguration().locale` for earlier Android version.
const char *getLocale(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx)
{
JavaVM *vm = (JavaVM *)java_vm;
JNIEnv *env = (JNIEnv *)jni_env;
jobject context = (jobject)ctx;
const jobject locale = getLocaleObject(env, context);
return toLanguageTag(env, locale);
}
const char *getLanguage(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx)
{
JavaVM *vm = (JavaVM *)java_vm;
JNIEnv *env = (JNIEnv *)jni_env;
jobject context = (jobject)ctx;
const jobject locale = getLocaleObject(env, context);
const jclass java_util_Locale = findClass(env, "java/util/Locale");
const jstring language =
(*env)->CallObjectMethod(
env,
locale,
(*env)->GetMethodID(env, java_util_Locale, "getLanguage", "()Ljava/lang/String;"));
return jstringToCharCopy(env, language);
}
const char *getRegion(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx)
{
JavaVM *vm = (JavaVM *)java_vm;
JNIEnv *env = (JNIEnv *)jni_env;
jobject context = (jobject)ctx;
const jobject locale = getLocaleObject(env, context);
const jclass java_util_Locale = findClass(env, "java/util/Locale");
const jstring country =
(*env)->CallObjectMethod(
env,
locale,
(*env)->GetMethodID(env, java_util_Locale, "getCountry", "()Ljava/lang/String;"));
return jstringToCharCopy(env, country);
}

@ -0,0 +1,115 @@
// +build android
package locale
/*
#cgo LDFLAGS: -landroid -llog
#include <stdlib.h>
const char *getLocales(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx);
const char *getLocale(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx);
const char *getLanguage(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx);
const char *getRegion(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx);
*/
import "C"
import (
"errors"
"strings"
"unsafe"
)
var (
errRunOnJVMNotSet error = errors.New("you first need to call SetRunOnJVM")
runOnJVM func(fn func(vm, env, ctx uintptr) error) error
)
// SetRunOnJVM sets the RunOnJVM function that will be called by this library.
// This can either be "golang.org/x/mobile/app".RunOnJVM or "github.com/fyne-io/mobile/app".RunOnJVM,
// depending on the mobile framework you're using (both can't be imported at the same time).
//
// RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv.
//
// RunOnJVM blocks until the call to fn is complete. Any Java
// exception or failure to attach to the JVM is returned as an error.
//
// The function fn takes vm, the current JavaVM*,
// env, the current JNIEnv*, and
// ctx, a jobject representing the global android.context.Context.
func SetRunOnJVM(fn func(fn func(vm, env, ctx uintptr) error) error) {
runOnJVM = fn
}
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
if runOnJVM == nil {
return "", errRunOnJVMNotSet
}
locale := ""
err := runOnJVM(func(vm, env, ctx uintptr) error {
chars := C.getLocale(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
locale = C.GoString(chars)
C.free(unsafe.Pointer(chars))
return nil
})
return locale, err
}
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
if runOnJVM == nil {
return nil, errRunOnJVMNotSet
}
locales := ""
err := runOnJVM(func(vm, env, ctx uintptr) error {
chars := C.getLocales(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
locales = C.GoString(chars)
C.free(unsafe.Pointer(chars))
return nil
})
return strings.Split(locales, ","), err
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
if runOnJVM == nil {
return "", errRunOnJVMNotSet
}
language := ""
err := runOnJVM(func(vm, env, ctx uintptr) error {
chars := C.getLanguage(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
language = C.GoString(chars)
C.free(unsafe.Pointer(chars))
return nil
})
return language, err
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
if runOnJVM == nil {
return "", errRunOnJVMNotSet
}
region := ""
err := runOnJVM(func(vm, env, ctx uintptr) error {
chars := C.getRegion(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
region = C.GoString(chars)
C.free(unsafe.Pointer(chars))
return nil
})
return region, err
}

@ -0,0 +1,95 @@
// +build darwin,!ios
package locale
import (
"fmt"
"os/exec"
"regexp"
"strings"
"syscall"
)
func execCommand(cmd string, args ...string) (status int, out []byte, err error) {
status = -1
command := exec.Command(cmd, args...)
// Execute the command and get the standard and error outputs
out, err = command.CombinedOutput()
if err != nil {
return
}
// Check the status code
if w, ok := command.ProcessState.Sys().(syscall.WaitStatus); ok {
status = w.ExitStatus()
}
return
}
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
_, output, err := execCommand("defaults", "read", "-g", "AppleLocale")
if err != nil {
return "", fmt.Errorf("cannot determine locale: %v (output: %s)", err, output)
}
return strings.TrimRight(strings.Replace(string(output), "_", "-", 1), "\n"), nil
}
// appleLanguagesRegex is used to parse the output of "defaults read -g AppleLanguages"
// e.g.:
// (
// "en-US",
// "fr-FR",
// "ja-JP"
// )
var appleLanguagesRegex = regexp.MustCompile(`"([a-z]{2}-[A-Z]{2})"`)
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
_, output, err := execCommand("defaults", "read", "-g", "AppleLanguages")
if err != nil {
return nil, fmt.Errorf("cannot determine locale: %v (output: %s)", err, output)
}
matches := appleLanguagesRegex.FindAllStringSubmatch(string(output), -1)
if len(matches) == 0 {
return nil, fmt.Errorf("invalid output from \"defaults read -g AppleLanguages\": %s", output)
}
locales := make([]string, 0, len(matches))
for _, match := range matches {
locales = append(locales, match[1])
}
return locales, nil
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
language := ""
locale, err := GetLocale()
if err == nil {
language, _ = splitLocale(locale)
}
return language, err
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
region := ""
locale, err := GetLocale()
if err == nil {
_, region = splitLocale(locale)
}
return region, err
}

@ -0,0 +1,51 @@
// +build ios
package locale
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework UIKit
const char *getLocale();
const char *getLocales();
*/
import "C"
import (
"strings"
)
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
return C.GoString(C.getLocale()), nil
}
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
return strings.Split(C.GoString(C.getLocales()), ","), nil
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
language := ""
locale, err := GetLocale()
if err == nil {
language, _ = splitLocale(locale)
}
return language, err
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
region := ""
locale, err := GetLocale()
if err == nil {
_, region = splitLocale(locale)
}
return region, err
}

@ -0,0 +1,17 @@
// +build ios
#import <UIKit/UIKit.h>
const char *getLocale()
{
NSString *locale = [[NSLocale preferredLanguages] firstObject];
return [locale UTF8String];
}
const char *getLocales()
{
NSString *locales = [[NSLocale preferredLanguages] componentsJoinedByString:@","];
return [locales UTF8String];
}

@ -0,0 +1,75 @@
// +build js,wasm
package locale
import (
"errors"
"syscall/js"
)
func getNavigatorObject() js.Value {
return js.Global().Get("navigator")
}
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
navigator := getNavigatorObject()
if navigator.IsUndefined() {
return "", errors.New("couldn't get window.navigator")
}
language := navigator.Get("language")
if language.IsUndefined() {
return "", errors.New("couldn't get window.navigator.language")
}
return language.String(), nil
}
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
navigator := getNavigatorObject()
if navigator.IsUndefined() {
return nil, errors.New("couldn't get window.navigator")
}
languages := navigator.Get("languages")
if languages.IsUndefined() {
return nil, errors.New("couldn't get window.navigator.languages")
}
locales := make([]string, 0, languages.Length())
// Convert the Javascript object to a string slice
for i := 0; i < languages.Length(); i++ {
locales = append(locales, languages.Index(i).String())
}
return locales, nil
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
language := ""
locale, err := GetLocale()
if err == nil {
language, _ = splitLocale(locale)
}
return language, err
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
region := ""
locale, err := GetLocale()
if err == nil {
_, region = splitLocale(locale)
}
return region, err
}

@ -0,0 +1,116 @@
// +build !windows,!darwin,!js,!android
package locale
import (
"os"
"strings"
)
func splitLocales(locales string) []string {
// If the user set different locales, they might be set in $LANGUAGE,
// separated by a colon
return strings.Split(locales, ":")
}
func getLangFromEnv() string {
locale := ""
// Check the following environment variables for the language information
// See https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
for _, env := range [...]string{"LC_ALL", "LC_MESSAGES", "LANG"} {
locale = os.Getenv(env)
if len(locale) > 0 {
break
}
}
if locale == "C" || locale == "POSIX" {
return locale
}
// Check LANGUAGE if localization is enabled (not set to "C")
// See https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
languages := os.Getenv("LANGUAGE")
if len(languages) > 0 {
return languages
}
return locale
}
func getUnixLocales() []string {
locale := getLangFromEnv()
if locale == "C" || locale == "POSIX" || len(locale) == 0 {
return nil
}
return splitLocales(locale)
}
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
unixLocales := getUnixLocales()
if unixLocales == nil {
return "", nil
}
language, region := splitLocale(unixLocales[0])
locale := language
if len(region) > 0 {
locale = strings.Join([]string{language, region}, "-")
}
return locale, nil
}
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
unixLocales := getUnixLocales()
if unixLocales == nil {
return nil, nil
}
locales := make([]string, 0, len(unixLocales))
for _, unixLocale := range unixLocales {
language, region := splitLocale(unixLocale)
locale := language
if len(region) > 0 {
locale = strings.Join([]string{language, region}, "-")
}
locales = append(locales, locale)
}
return locales, nil
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
language := ""
unixLocales := getUnixLocales()
if unixLocales == nil {
return "", nil
}
language, _ = splitLocale(unixLocales[0])
return language, nil
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
region := ""
unixLocales := getUnixLocales()
if unixLocales == nil {
return "", nil
}
_, region = splitLocale(unixLocales[0])
return region, nil
}

@ -0,0 +1,101 @@
// +build windows
package locale
import (
"fmt"
"unsafe"
"golang.org/x/sys/windows"
)
// LocaleNameMaxLength is the maximum length of a locale name on Windows.
// See https://docs.microsoft.com/en-us/windows/win32/intl/locale-name-constants.
const LocaleNameMaxLength uint32 = 85
func getWindowsLocaleFromProc(syscall string) (string, error) {
dll, err := windows.LoadDLL("kernel32")
if err != nil {
return "", fmt.Errorf("could not find the kernel32 DLL: %v", err)
}
proc, err := dll.FindProc(syscall)
if err != nil {
return "", fmt.Errorf("could not find the %s proc in kernel32: %v", syscall, err)
}
buffer := make([]uint16, LocaleNameMaxLength)
// See https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename
// and https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getsystemdefaultlocalename
// GetUserDefaultLocaleName and GetSystemDefaultLocaleName both take a buffer and a buffer size,
// and return the length of the locale name (0 if not found).
ret, _, err := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LocaleNameMaxLength))
if ret == 0 {
return "", fmt.Errorf("locale not found when calling %s: %v", syscall, err)
}
return windows.UTF16ToString(buffer), nil
}
func getWindowsLocale() (string, error) {
var (
locale string
err error
)
for _, proc := range [...]string{"GetUserDefaultLocaleName", "GetSystemDefaultLocaleName"} {
locale, err = getWindowsLocaleFromProc(proc)
if err == nil {
return locale, err
}
}
return locale, err
}
// GetLocale retrieves the IETF BCP 47 language tag set on the system.
func GetLocale() (string, error) {
locale, err := getWindowsLocale()
if err != nil {
return "", fmt.Errorf("cannot determine locale: %v", err)
}
return locale, err
}
// GetLocales retrieves the IETF BCP 47 language tags set on the system.
func GetLocales() ([]string, error) {
locale, err := GetLocale()
if err != nil {
return nil, err
}
return []string{locale}, nil
}
// GetLanguage retrieves the IETF BCP 47 language tag set on the system and
// returns the language part of the tag.
func GetLanguage() (string, error) {
language := ""
locale, err := GetLocale()
if err == nil {
language, _ = splitLocale(locale)
}
return language, err
}
// GetRegion retrieves the IETF BCP 47 language tag set on the system and
// returns the region part of the tag.
func GetRegion() (string, error) {
region := ""
locale, err := GetLocale()
if err == nil {
_, region = splitLocale(locale)
}
return region, err
}

@ -0,0 +1,27 @@
// +build !android
package locale
import (
"strings"
)
// SetRunOnJVM is a noop, this function is only valid on Android
func SetRunOnJVM(fn func(fn func(vm, env, ctx uintptr) error) error) {}
func splitLocale(locale string) (string, string) {
// Remove the encoding, if present
formattedLocale := strings.Split(locale, ".")[0]
// Normalize by replacing the hyphens with underscores
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
// Split at the underscore
split := strings.Split(formattedLocale, "_")
language := split[0]
territory := ""
if len(split) > 1 {
territory = split[1]
}
return language, territory
}

@ -41,6 +41,9 @@ github.com/go-toast/toast
# github.com/godbus/dbus/v5 v5.0.4
## explicit; go 1.12
github.com/godbus/dbus/v5
# github.com/goodsign/monday v1.0.0
## explicit; go 1.13
github.com/goodsign/monday
# github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1
## explicit
github.com/gopherjs/gopherjs/js
@ -50,6 +53,9 @@ github.com/gopherjs/gopherwasm/js
# github.com/inconshreveable/mousetrap v1.0.0
## explicit
github.com/inconshreveable/mousetrap
# github.com/jeandeaual/go-locale v0.0.0-20210323163322-5cf4ff553a8d
## explicit; go 1.12
github.com/jeandeaual/go-locale
# github.com/maruel/panicparse v1.6.1
## explicit; go 1.11
github.com/maruel/panicparse/stack

Loading…
Cancel
Save