Skip to content

Commit 7937d4d

Browse files
authored
feat: support other languages (#4)
* feat: add support for all language supported by tyler-smith/go-bip39 * fix: use full language flag in cmd * fix: use t.Cleanup to reset language * fix: add more language identifier for Chinese
1 parent 2e724bd commit 7937d4d

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

cmd/melt/main.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/muesli/reflow/wordwrap"
1919
"github.com/muesli/roff"
2020
"github.com/muesli/termenv"
21+
"github.com/tyler-smith/go-bip39"
22+
"github.com/tyler-smith/go-bip39/wordlists"
2123
"golang.org/x/crypto/ssh"
2224
"golang.org/x/term"
2325
)
@@ -39,6 +41,9 @@ var (
3941
Padding(1, 2)
4042
keyPathStyle = lipgloss.NewStyle().Foreground(violet)
4143

44+
mnemonic string
45+
language string
46+
4247
rootCmd = &coral.Command{
4348
Use: "melt",
4449
Example: ` melt ~/.ssh/id_ed25519
@@ -51,6 +56,10 @@ be used to rebuild your public and private keys.`,
5156
Args: coral.ExactArgs(1),
5257
SilenceUsage: true,
5358
RunE: func(cmd *coral.Command, args []string) error {
59+
if err := setLanguage(language); err != nil {
60+
return err
61+
}
62+
5463
mnemonic, err := backup(args[0], nil)
5564
if err != nil {
5665
return err
@@ -67,8 +76,12 @@ be used to rebuild your public and private keys.`,
6776

6877
// Build formatted restore command
6978
const cmdEOL = " \\"
79+
var lang string
80+
if language != "en" {
81+
lang = fmt.Sprintf(" --language %s", language)
82+
}
7083
cmd := wordwrap.String(
71-
os.Args[0]+` restore ./my-key --seed "`+mnemonic+`"`,
84+
os.Args[0]+` restore`+lang+` ./my-key --seed "`+mnemonic+`"`,
7285
w-lipgloss.Width(cmdEOL)-baseStyle.GetHorizontalFrameSize()*2,
7386
)
7487
leftPad := strings.Repeat(" ", baseStyle.GetMarginLeft())
@@ -91,7 +104,6 @@ be used to rebuild your public and private keys.`,
91104
},
92105
}
93106

94-
mnemonic string
95107
restoreCmd = &coral.Command{
96108
Use: "restore",
97109
Short: "Recreate a key using the given seed phrase",
@@ -100,6 +112,10 @@ be used to rebuild your public and private keys.`,
100112
Aliases: []string{"res", "r"},
101113
Args: coral.ExactArgs(1),
102114
RunE: func(cmd *coral.Command, args []string) error {
115+
if err := setLanguage(language); err != nil {
116+
return err
117+
}
118+
103119
if err := restore(maybeFile(mnemonic), args[0]); err != nil {
104120
return err
105121
}
@@ -131,6 +147,7 @@ be used to rebuild your public and private keys.`,
131147
)
132148

133149
func init() {
150+
rootCmd.PersistentFlags().StringVarP(&language, "language", "l", "en", "Language")
134151
rootCmd.AddCommand(restoreCmd, manCmd)
135152

136153
restoreCmd.PersistentFlags().StringVarP(&mnemonic, "seed", "s", "-", "Seed phrase")
@@ -238,3 +255,30 @@ func completeColor(truecolor, ansi256, ansi string) string {
238255
}
239256
return ansi
240257
}
258+
259+
// setLanguage sets the language of the big39 mnemonic seed.
260+
func setLanguage(language string) error {
261+
switch strings.ToLower(language) {
262+
case "chinese-simplified", "zh", "zh_HANS":
263+
bip39.SetWordList(wordlists.ChineseSimplified)
264+
case "chinese-traditional", "zh_HANT":
265+
bip39.SetWordList(wordlists.ChineseTraditional)
266+
case "czech", "cs":
267+
bip39.SetWordList(wordlists.Czech)
268+
case "english", "en":
269+
bip39.SetWordList(wordlists.English)
270+
case "french", "fr":
271+
bip39.SetWordList(wordlists.French)
272+
case "italian", "it":
273+
bip39.SetWordList(wordlists.Italian)
274+
case "japanese", "ja":
275+
bip39.SetWordList(wordlists.Japanese)
276+
case "korean", "ko":
277+
bip39.SetWordList(wordlists.Korean)
278+
case "spanish", "es":
279+
bip39.SetWordList(wordlists.Spanish)
280+
default:
281+
return fmt.Errorf("this language is not supported")
282+
}
283+
return nil
284+
}

cmd/melt/main_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,47 @@ func TestBackupRestoreKnownKey(t *testing.T) {
7676
})
7777
}
7878

79+
func TestBackupRestoreKnownKeyInJapanse(t *testing.T) {
80+
const expectedMnemonic = `
81+
いきおい ざるそば えもの せんめんじょ てあみ ていねい はったつ
82+
ろこつ すあし のぞく かまう ほくろ らくご けぶかい たおす よゆう
83+
ひめじし くたびれる ぐんたい なわばり にかい えほん せなか
84+
そいとげる
85+
`
86+
const expectedSum = "ba34175ef608633b29f046b40cce596dd221347b77abba40763eef2e7ae51fe9"
87+
const expectedFingerprint = "SHA256:tX0ZrsNLIB/ZlRK3vy/HsWIIkyBNhYhCSGmtqtxJcWo"
88+
89+
// set language to Japanse
90+
setLanguage("japanese")
91+
92+
// set language back to English
93+
t.Cleanup(func() {
94+
setLanguage("english")
95+
})
96+
97+
t.Run("backup", func(t *testing.T) {
98+
mnemonic, err := backup("testdata/id_ed25519", nil)
99+
is := is.New(t)
100+
is.NoErr(err)
101+
is.Equal(mnemonic, strings.Join(strings.Fields(expectedMnemonic), " "))
102+
})
103+
104+
t.Run("restore", func(t *testing.T) {
105+
is := is.New(t)
106+
path := filepath.Join(t.TempDir(), "key")
107+
is.NoErr(restore(expectedMnemonic, path))
108+
is.Equal(expectedSum, sha256sum(t, path+".pub"))
109+
110+
bts, err := os.ReadFile(path)
111+
is.NoErr(err)
112+
113+
k, err := ssh.ParsePrivateKey(bts)
114+
is.NoErr(err)
115+
116+
is.Equal(expectedFingerprint, ssh.FingerprintSHA256(k.PublicKey()))
117+
})
118+
}
119+
79120
func TestMaybeFile(t *testing.T) {
80121
t.Run("is a file", func(t *testing.T) {
81122
is := is.New(t)

0 commit comments

Comments
 (0)