Skip to content

Commit 52073f7

Browse files
Add localization support for go-sqlcmd (#207)
Add localization support for go-sqlcmd This commit adds localization framework and support for languages en-us, de-de, fr-fr, es-es, pt-br, ko-kr, zh-cn, zh-tw, it-it, ja-jp, ru-ru, zh-hans, zh-hant. Translations have been taken from ODBC sqlcmd.rll.lcl for corresponding languages.
1 parent 5f01096 commit 52073f7

27 files changed

+5543
-18
lines changed

cmd/sqlcmd/sqlcmd.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212

1313
"github.com/microsoft/go-mssqldb/azuread"
14+
"github.com/microsoft/go-sqlcmd/internal/localizer"
1415
"github.com/microsoft/go-sqlcmd/pkg/console"
1516
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
1617
)
@@ -66,17 +67,17 @@ type SQLCmdArguments struct {
6667
// Validate accounts for settings not described by Kong attributes
6768
func (a *SQLCmdArguments) Validate() error {
6869
if a.PacketSize != 0 && (a.PacketSize < 512 || a.PacketSize > 32767) {
69-
return fmt.Errorf(`'-a %d': Packet size has to be a number between 512 and 32767.`, a.PacketSize)
70+
return localizer.Errorf(`'-a %d': Packet size has to be a number between 512 and 32767.`, a.PacketSize)
7071
}
7172
// Ignore 0 even though it's technically an invalid input
7273
if a.Headers < -1 {
73-
return fmt.Errorf(`'-h %d': header value must be either -1 or a value between 1 and 2147483647`, a.Headers)
74+
return localizer.Errorf(`'-h %d': header value must be either -1 or a value between 1 and 2147483647`, a.Headers)
7475
}
7576
if a.ScreenWidth != nil && (*a.ScreenWidth < 9 || *a.ScreenWidth > 65535) {
76-
return fmt.Errorf(`'-w %d': value must be greater than 8 and less than 65536.`, *a.ScreenWidth)
77+
return localizer.Errorf(`'-w %d': value must be greater than 8 and less than 65536.`, *a.ScreenWidth)
7778
}
7879
if a.Password != "" {
79-
return fmt.Errorf(`'-P' is obsolete. The initial passwords must be set using the SQLCMDPASSWORD environment variable or entered at the password prompt.`)
80+
return localizer.Errorf(`'-P' is obsolete. The initial passwords must be set using the SQLCMDPASSWORD environment variable or entered at the password prompt.`)
8081
}
8182
return nil
8283
}
@@ -258,7 +259,7 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
258259
if args.BatchTerminator != "GO" {
259260
err = s.Cmd.SetBatchTerminator(args.BatchTerminator)
260261
if err != nil {
261-
err = fmt.Errorf("invalid batch terminator '%s'", args.BatchTerminator)
262+
err = localizer.Errorf("invalid batch terminator '%s'", args.BatchTerminator)
262263
}
263264
}
264265
if err != nil {

internal/localizer/localizer.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package localizer
2+
3+
import (
4+
// Import the internal/translations so that its init() function
5+
// is run. It's really important that we do this here so that the
6+
// default message catalog is updated to use our translations
7+
// *before* we initialize the message.Printer instances below.
8+
9+
"fmt"
10+
"os"
11+
"strings"
12+
13+
_ "github.com/microsoft/go-sqlcmd/internal/translations"
14+
"golang.org/x/text/language"
15+
"golang.org/x/text/message"
16+
)
17+
18+
var Translator *message.Printer
19+
20+
var supportedLanguages = map[string]string{
21+
"de-de": "de-DE",
22+
"fr-fr": "fr-FR",
23+
"en-us": "en-US",
24+
"zh-hans": "zh-CN",
25+
"zh-cn": "zh-CN",
26+
"zh-hant": "zh-TW",
27+
"zh-tw": "zh-TW",
28+
"it-it": "it-IT",
29+
"ja-jp": "ja-JP",
30+
"ko-kr": "ko-KR",
31+
"pt-br": "pt-BR",
32+
"ru-ru": "ru-RU",
33+
"es-es": "es-ES",
34+
}
35+
36+
// init() initializes the language automatically
37+
// based on env var SQLCMD_LANG which expects language
38+
// tag such as en-us, de-de, fr-ch, etc.
39+
func init() {
40+
localeName := strings.ToLower(os.Getenv("SQLCMD_LANG"))
41+
if _, ok := supportedLanguages[localeName]; !ok {
42+
localeName = "en-us"
43+
}
44+
Translator = message.NewPrinter(language.MustParse(supportedLanguages[localeName]))
45+
}
46+
47+
// Errorf() is wrapper function to create localized errors
48+
func Errorf(format string, a ...any) error {
49+
errMsg := Translator.Sprintf(format, a...)
50+
return fmt.Errorf(errMsg)
51+
}
52+
53+
// Sprintf() is wrapper function to create localized string
54+
func Sprintf(key message.Reference, args ...interface{}) string {
55+
return Translator.Sprintf(key, args...)
56+
}

internal/translations/catalog.go

+284
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
{
2+
"language": "de-DE",
3+
"messages": [
4+
{
5+
"id": "'-a {PacketSize}': Packet size has to be a number between 512 and 32767.",
6+
"message": "'-a {PacketSize}': Packet size has to be a number between 512 and 32767.",
7+
"translation": "",
8+
"placeholders": [
9+
{
10+
"id": "PacketSize",
11+
"string": "%[1]d",
12+
"type": "int",
13+
"underlyingType": "int",
14+
"argNum": 1,
15+
"expr": "a.PacketSize"
16+
}
17+
]
18+
},
19+
{
20+
"id": "'-h {Headers}': header value must be either -1 or a value between 1 and 2147483647",
21+
"message": "'-h {Headers}': header value must be either -1 or a value between 1 and 2147483647",
22+
"translation": "'-h {Headers}': Der Headerwert muss entweder -1 oder ein Wert zwischen -1 und 2147483647 sein",
23+
"placeholders": [
24+
{
25+
"id": "Headers",
26+
"string": "%[1]d",
27+
"type": "int",
28+
"underlyingType": "int",
29+
"argNum": 1,
30+
"expr": "a.Headers"
31+
}
32+
]
33+
},
34+
{
35+
"id": "'-w {ScreenWidth}': value must be greater than 8 and less than 65536.",
36+
"message": "'-w {ScreenWidth}': value must be greater than 8 and less than 65536.",
37+
"translation": "'-w {ScreenWidth}': Der Wert muss größer als 8 und kleiner als 65536 sein.",
38+
"placeholders": [
39+
{
40+
"id": "ScreenWidth",
41+
"string": "%[1]d",
42+
"type": "int",
43+
"underlyingType": "int",
44+
"argNum": 1,
45+
"expr": "*a.ScreenWidth"
46+
}
47+
]
48+
},
49+
{
50+
"id": "'-P' is obsolete. The initial passwords must be set using the SQLCMDPASSWORD environment variable or entered at the password prompt.",
51+
"message": "'-P' is obsolete. The initial passwords must be set using the SQLCMDPASSWORD environment variable or entered at the password prompt.",
52+
"translation": ""
53+
},
54+
{
55+
"id": "invalid batch terminator '{BatchTerminator}'",
56+
"message": "invalid batch terminator '{BatchTerminator}'",
57+
"translation": "Ungültiges Batchabschlusszeichen '{BatchTerminator}'",
58+
"placeholders": [
59+
{
60+
"id": "BatchTerminator",
61+
"string": "%[1]s",
62+
"type": "string",
63+
"underlyingType": "string",
64+
"argNum": 1,
65+
"expr": "args.BatchTerminator"
66+
}
67+
]
68+
},
69+
{
70+
"id": "Sqlcmd: Error:",
71+
"message": "Sqlcmd: Error:",
72+
"translation": "Sqlcmd: Fehler:"
73+
},
74+
{
75+
"id": "Sqlcmd: Warning:",
76+
"message": "Sqlcmd: Warning:",
77+
"translation": "Sqlcmd: Warnung:"
78+
},
79+
{
80+
"id": "ED and !!\u003ccommand\u003e commands, startup script, and environment variables are disabled",
81+
"message": "ED and !!\u003ccommand\u003e commands, startup script, and environment variables are disabled",
82+
"translation": "Die Befehle ED und !!\u003ccommand\u003e, Startskript und Umgebungsvariablen sind deaktiviert"
83+
},
84+
{
85+
"id": "The scripting variable: '{Variable}' is read-only",
86+
"message": "The scripting variable: '{Variable}' is read-only",
87+
"translation": "Die '{Variable}'-Skriptvariable ist schreibgeschützt",
88+
"placeholders": [
89+
{
90+
"id": "Variable",
91+
"string": "%[1]s",
92+
"type": "string",
93+
"underlyingType": "string",
94+
"argNum": 1,
95+
"expr": "variable"
96+
}
97+
]
98+
},
99+
{
100+
"id": "'{Variable}' scripting variable not defined.",
101+
"message": "'{Variable}' scripting variable not defined.",
102+
"translation": "Die '{Variable}'-Skriptvariable ist nicht definiert.",
103+
"placeholders": [
104+
{
105+
"id": "Variable",
106+
"string": "%[1]s",
107+
"type": "string",
108+
"underlyingType": "string",
109+
"argNum": 1,
110+
"expr": "variable"
111+
}
112+
]
113+
},
114+
{
115+
"id": "The environment variable: '{Variable}' has invalid value: '{EnvVal}'.",
116+
"message": "The environment variable: '{Variable}' has invalid value: '{EnvVal}'.",
117+
"translation": "Die Umgebungsvariable: '{Variable}' weist einen ungültigen Wert auf: '{EnvVal}'.",
118+
"placeholders": [
119+
{
120+
"id": "Variable",
121+
"string": "%[1]s",
122+
"type": "string",
123+
"underlyingType": "string",
124+
"argNum": 1,
125+
"expr": "variable"
126+
},
127+
{
128+
"id": "EnvVal",
129+
"string": "%[2]s",
130+
"type": "string",
131+
"underlyingType": "string",
132+
"argNum": 2,
133+
"expr": "envVal"
134+
}
135+
]
136+
},
137+
{
138+
"id": "Syntax error at line {LineNumber} near command '{Command}'.",
139+
"message": "Syntax error at line {LineNumber} near command '{Command}'.",
140+
"translation": "Syntaxfehler in Zeile {LineNumber} in der Nähe von Befehl '{Command}'.",
141+
"placeholders": [
142+
{
143+
"id": "LineNumber",
144+
"string": "%[1]d",
145+
"type": "uint",
146+
"underlyingType": "uint",
147+
"argNum": 1,
148+
"expr": "e.LineNumber"
149+
},
150+
{
151+
"id": "Command",
152+
"string": "%[2]s",
153+
"type": "string",
154+
"underlyingType": "string",
155+
"argNum": 2,
156+
"expr": "e.Command"
157+
}
158+
]
159+
},
160+
{
161+
"id": "{ErrorPrefix} Error occurred while opening or operating on file {Filepath} (Reason: {Error}).",
162+
"message": "{ErrorPrefix} Error occurred while opening or operating on file {Filepath} (Reason: {Error}).",
163+
"translation": "{ErrorPrefix} Fehler beim Öffnen oder Verarbeiten von Datei {Filepath} (Ursache: {Error}).",
164+
"placeholders": [
165+
{
166+
"id": "ErrorPrefix",
167+
"string": "%[1]s",
168+
"type": "string",
169+
"underlyingType": "string",
170+
"argNum": 1,
171+
"expr": "ErrorPrefix"
172+
},
173+
{
174+
"id": "Filepath",
175+
"string": "%[2]s",
176+
"type": "string",
177+
"underlyingType": "string",
178+
"argNum": 2,
179+
"expr": "filepath"
180+
},
181+
{
182+
"id": "Error",
183+
"string": "%[3]s",
184+
"type": "string",
185+
"underlyingType": "string",
186+
"argNum": 3,
187+
"expr": "err.Error()"
188+
}
189+
]
190+
},
191+
{
192+
"id": "{ErrorPrefix}Syntax error at line {LineNumber}",
193+
"message": "{ErrorPrefix}Syntax error at line {LineNumber}",
194+
"translation": "{ErrorPrefix}Syntaxfehler in Zeile {LineNumber}",
195+
"placeholders": [
196+
{
197+
"id": "ErrorPrefix",
198+
"string": "%[1]s",
199+
"type": "string",
200+
"underlyingType": "string",
201+
"argNum": 1,
202+
"expr": "ErrorPrefix"
203+
},
204+
{
205+
"id": "LineNumber",
206+
"string": "%[2]d",
207+
"type": "uint",
208+
"underlyingType": "uint",
209+
"argNum": 2,
210+
"expr": "lineNumber"
211+
}
212+
]
213+
},
214+
{
215+
"id": "Invalid variable identifier {Name}",
216+
"message": "Invalid variable identifier {Name}",
217+
"translation": "",
218+
"placeholders": [
219+
{
220+
"id": "Name",
221+
"string": "%[1]s",
222+
"type": "string",
223+
"underlyingType": "string",
224+
"argNum": 1,
225+
"expr": "name"
226+
}
227+
]
228+
},
229+
{
230+
"id": "Invalid variable value {Val}",
231+
"message": "Invalid variable value {Val}",
232+
"translation": "",
233+
"placeholders": [
234+
{
235+
"id": "Val",
236+
"string": "%[1]s",
237+
"type": "string",
238+
"underlyingType": "string",
239+
"argNum": 1,
240+
"expr": "val"
241+
}
242+
]
243+
}
244+
]
245+
}

0 commit comments

Comments
 (0)