55 "strings"
66 "time"
77
8+ "golang.org/x/text/unicode/norm"
9+
810 "github.com/eljojo/rememory/internal/core"
911 "github.com/eljojo/rememory/internal/project"
1012 "github.com/eljojo/rememory/internal/translations"
@@ -27,6 +29,22 @@ type ReadmeData struct {
2729 Language string // Bundle language (e.g. "en", "es"); defaults to "en"
2830}
2931
32+ // writeWordGrid writes a two-column word grid to the string builder.
33+ // Words are NFC-normalized so accented characters are precomposed
34+ // (BIP39 word lists may store them in NFD form).
35+ func writeWordGrid (sb * strings.Builder , words []string ) {
36+ half := (len (words ) + 1 ) / 2
37+ for i := 0 ; i < half ; i ++ {
38+ left := fmt .Sprintf ("%2d. %-18s" , i + 1 , norm .NFC .String (words [i ]))
39+ if i + half < len (words ) {
40+ right := fmt .Sprintf ("%2d. %s" , i + half + 1 , norm .NFC .String (words [i + half ]))
41+ sb .WriteString (fmt .Sprintf ("%s%s\n " , left , right ))
42+ } else {
43+ sb .WriteString (left + "\n " )
44+ }
45+ }
46+ }
47+
3048// GenerateReadme creates the README.txt content with all embedded information.
3149func GenerateReadme (data ReadmeData ) string {
3250 lang := data .Language
@@ -118,20 +136,26 @@ func GenerateReadme(data ReadmeData) string {
118136 sb .WriteString ("--------------------------------------------------------------------------------\n " )
119137
120138 // Word list (primary human-readable format)
121- words , _ := data .Share .Words ()
122- if len (words ) > 0 {
123- sb .WriteString (fmt .Sprintf ("%s\n \n " , t ("recovery_words_title" , len (words ))))
124- half := (len (words ) + 1 ) / 2
125- for i := 0 ; i < half ; i ++ {
126- left := fmt .Sprintf ("%2d. %-14s" , i + 1 , words [i ])
127- if i + half < len (words ) {
128- right := fmt .Sprintf ("%2d. %s" , i + half + 1 , words [i + half ])
129- sb .WriteString (fmt .Sprintf ("%s%s\n " , left , right ))
130- } else {
131- sb .WriteString (left + "\n " )
132- }
139+ nativeWords , _ := data .Share .WordsForLang (core .Lang (lang ))
140+ if len (nativeWords ) > 0 {
141+ if lang != "en" {
142+ // Non-English: show native language grid first, then English
143+ langName := t ("lang_" + lang )
144+ sb .WriteString (fmt .Sprintf ("%s\n \n " , t ("recovery_words_title_lang" , len (nativeWords ), langName )))
145+ writeWordGrid (& sb , nativeWords )
146+ sb .WriteString (fmt .Sprintf ("\n %s\n \n " , t ("recovery_words_hint" )))
147+
148+ // English fallback grid
149+ englishWords , _ := data .Share .Words ()
150+ sb .WriteString (fmt .Sprintf ("%s\n \n " , t ("recovery_words_title_english" , len (englishWords ))))
151+ writeWordGrid (& sb , englishWords )
152+ sb .WriteString (fmt .Sprintf ("\n %s\n \n " , t ("recovery_words_dual_hint" )))
153+ } else {
154+ // English only: single grid
155+ sb .WriteString (fmt .Sprintf ("%s\n \n " , t ("recovery_words_title" , len (nativeWords ))))
156+ writeWordGrid (& sb , nativeWords )
157+ sb .WriteString (fmt .Sprintf ("\n %s\n \n " , t ("recovery_words_hint" )))
133158 }
134- sb .WriteString (fmt .Sprintf ("\n %s\n \n " , t ("recovery_words_hint" )))
135159 }
136160
137161 // PEM block (machine-readable format)
0 commit comments