mirror of https://github.com/miguelmota/cointop
Merge branch 'lyricnz-feature/locale-dates'
commit
4ba9f52a87
@ -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,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 "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,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,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,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
|
||||
}
|
Loading…
Reference in New Issue