-
Notifications
You must be signed in to change notification settings - Fork 3
/
formatter.go
162 lines (149 loc) · 4.13 KB
/
formatter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) 2020 Bojan Zivanovic and contributors
// SPDX-License-Identifier: MIT
package address
import (
"html"
"strings"
)
// Formatter formats addresses for display.
type Formatter struct {
locale Locale
// CountryMapper maps country codes to country names.
// Can be used to retrieve country names from another (localized) source.
// Defaults to a function that uses English country names included in the package.
CountryMapper func(countryCode string, locale Locale) string
// NoCountry turns off displaying the country name.
// Defaults to false.
NoCountry bool
// WrapperElement is the wrapper HTML element.
// Defaults to "p".
WrapperElement string
// WrapperClass is the wrapper HTML class.
// Defaults to "address".
WrapperClass string
}
// NewFormatter creates a new formatter for the given locale.
func NewFormatter(locale Locale) *Formatter {
f := &Formatter{
locale: locale,
CountryMapper: func(countryCode string, locale Locale) string {
return countries[countryCode]
},
WrapperElement: "p",
WrapperClass: "address",
}
return f
}
// Locale returns the locale.
func (f *Formatter) Locale() Locale {
return f.locale
}
// Format formats the given address.
func (f *Formatter) Format(addr Address) string {
if addr.IsEmpty() {
return ""
}
format := GetFormat(addr.CountryCode)
layout := format.SelectLayout(f.locale)
countryBefore := (layout == format.LocalLayout)
countryAfter := (layout != format.LocalLayout)
country := ""
if !f.NoCountry {
country = html.EscapeString(f.CountryMapper(addr.CountryCode, f.locale))
country = `<span class="country" data-value="` + addr.CountryCode + `">` + country + `</span>`
}
values := f.getValues(addr)
for field, value := range values {
if value != "" {
value = html.EscapeString(value)
value = `<span class="` + f.getClass(field) + `">` + value + `</span>`
values[field] = value
}
}
sb := strings.Builder{}
sb.Grow(200)
sb.WriteString(`<` + f.WrapperElement + ` class="`)
sb.WriteString(f.WrapperClass)
sb.WriteString(`" translate="no">` + "\n")
if !f.NoCountry && countryBefore {
sb.WriteString(country)
sb.WriteString("<br>\n")
}
f.writeValues(&sb, layout, values)
if !f.NoCountry && countryAfter {
sb.WriteString("<br>\n")
sb.WriteString(country)
}
sb.WriteString("\n</" + f.WrapperElement + ">")
return sb.String()
}
// getClass returns the HTML class for the given field.
func (f *Formatter) getClass(field Field) string {
var class string
switch field {
case FieldLine1:
class = "line1"
case FieldLine2:
class = "line2"
case FieldLine3:
class = "line3"
case FieldSublocality:
class = "sublocality"
case FieldLocality:
class = "locality"
case FieldRegion:
class = "region"
case FieldPostalCode:
class = "postal-code"
}
return class
}
// getValues returns all values for the given address, keyed by field.
//
// Region IDs are replaced by region names if available.
func (f *Formatter) getValues(addr Address) map[Field]string {
values := map[Field]string{
FieldLine1: addr.Line1,
FieldLine2: addr.Line2,
FieldLine3: addr.Line3,
FieldSublocality: addr.Sublocality,
FieldLocality: addr.Locality,
FieldRegion: addr.Region,
FieldPostalCode: addr.PostalCode,
}
format := GetFormat(addr.CountryCode)
regions := format.SelectRegions(f.locale)
if !format.ShowRegionID && regions.Len() > 0 {
region, ok := regions.Get(addr.Region)
if ok {
values[FieldRegion] = region
}
}
return values
}
// writeValues inserts values into the layout and writes it to b.
//
// Tokens of empty fields are removed, as are their preceeding chars.
// For example: "%L, %P" becomes "%L" when %P has no value.
func (f *Formatter) writeValues(b *strings.Builder, layout string, values map[Field]string) {
prev := 0
for i := 0; i < len(layout); i++ {
if layout[i] != '%' {
continue
}
j, k := i+1, i+2
field := Field(layout[j:k])
if values[field] != "" {
prefix := layout[prev:i]
for l := 0; l < len(prefix); l++ {
if prefix[l] == '\n' {
// Prepend <br> to each newline.
b.WriteString("<br>")
}
b.WriteByte(prefix[l])
}
b.WriteString(values[field])
}
prev = k
}
}