cmd/gotext: allow const identifiers as keys

Change-Id: Idbcbed5103101e808ad7e7d2ac14de7128b25d62
Reviewed-on: https://go-review.googlesource.com/79577
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/cmd/gotext/examples/main.go b/cmd/gotext/examples/main.go
index d71bbd6..e1a84e0 100644
--- a/cmd/gotext/examples/main.go
+++ b/cmd/gotext/examples/main.go
@@ -23,8 +23,8 @@
 
 	p.Print("Hello ", person, " in ", place, "!\n")
 
-	// Greet a city.
-	p.Print("Hello city!\n")
+	// Greet everyone.
+	p.Printf("Hello world!\n")
 
 	city := "Amsterdam"
 	// Greet a city.
@@ -63,8 +63,17 @@
 	// Numeric var
 	p.Printf("%d more files remaining!", n)
 
+	// Infer better names from type names.
 	type referralCode int
 
-	c := referralCode(5)
+	const c = referralCode(5)
 	p.Printf("Use the following code for your discount: %d\n", c)
+
+	// Using a constant for a message will cause the constant name to be
+	// added as an identifier, allowing for stable message identifiers.
+
+	// Explain that a device is out of order.
+	const msgOutOfOrder = "%s is out of order!" // FOO
+	const device = "Soda machine"
+	p.Printf(msgOutOfOrder, device)
 }
diff --git a/cmd/gotext/examples/textdata/gotext_en.out.json b/cmd/gotext/examples/textdata/gotext_en.out.json
index 61de664..e6ebe5d 100755
--- a/cmd/gotext/examples/textdata/gotext_en.out.json
+++ b/cmd/gotext/examples/textdata/gotext_en.out.json
@@ -1,6 +1,15 @@
 [
     {
         "key": [
+            "Hello world!\n"
+        ],
+        "message": {
+            "msg": "Hello world!\n"
+        },
+        "position": "golang.org/x/text/cmd/gotext/examples/main.go:27:10"
+    },
+    {
+        "key": [
             "Hello %s!\n"
         ],
         "message": {
@@ -177,9 +186,35 @@
                 "type": "golang.org/x/text/cmd/gotext/examples.referralCode",
                 "underlyingType": "int",
                 "expr": "c",
-                "position": "golang.org/x/text/cmd/gotext/examples/main.go:69:61"
+                "value": "5",
+                "position": "golang.org/x/text/cmd/gotext/examples/main.go:70:61"
             }
         ],
-        "position": "golang.org/x/text/cmd/gotext/examples/main.go:69:10"
+        "position": "golang.org/x/text/cmd/gotext/examples/main.go:70:10"
+    },
+    {
+        "key": [
+            "msgOutOfOrder",
+            "%s is out of order!"
+        ],
+        "message": {
+            "msg": "{Device} is out of order!"
+        },
+        "comment": "FOO\n",
+        "args": [
+            {
+                "id": "Device",
+                "argNum": 1,
+                "format": [
+                    "%s"
+                ],
+                "type": "string",
+                "underlyingType": "string",
+                "expr": "device",
+                "value": "\"Soda machine\"",
+                "position": "golang.org/x/text/cmd/gotext/examples/main.go:78:26"
+            }
+        ],
+        "position": "golang.org/x/text/cmd/gotext/examples/main.go:78:10"
     }
 ]
\ No newline at end of file
diff --git a/cmd/gotext/extract.go b/cmd/gotext/extract.go
index 88c7513..ac318d1 100644
--- a/cmd/gotext/extract.go
+++ b/cmd/gotext/extract.go
@@ -124,7 +124,17 @@
 					// is not a string, multiple keys may be defined.
 					return true
 				}
-				key := []string{fmtMsg}
+				comment := ""
+				key := []string{}
+				if ident, ok := args[0].(*ast.Ident); ok {
+					key = append(key, ident.Name)
+					if v, ok := ident.Obj.Decl.(*ast.ValueSpec); ok && v.Comment != nil {
+						// TODO: get comment above ValueSpec as well
+						comment = v.Comment.Text()
+					}
+				}
+
+				key = append(key, fmtMsg)
 				arguments := []Argument{}
 				args = args[1:]
 				simArgs := make([]interface{}, len(args))
@@ -174,12 +184,16 @@
 					}
 				}
 
+				if c := getComment(call.Args[0]); c != "" {
+					comment = c
+				}
+
 				messages = append(messages, Message{
 					Key:      key,
 					Position: posString(conf, info, call.Lparen),
 					Message:  Text{Msg: msg},
 					// TODO(fix): this doesn't get the before comment.
-					Comment: getComment(call.Args[0]),
+					Comment: comment,
 					Args:    arguments,
 				})
 				return true