Skip to content

Commit

Permalink
Implement text translations
Browse files Browse the repository at this point in the history
  • Loading branch information
xanni committed Sep 23, 2024
1 parent 196b3cb commit c4b4ab1
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 45 deletions.
33 changes: 15 additions & 18 deletions edits/edits.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"unicode"
"unicode/utf8"

"git.sericyb.com.au/jotty/i18n"
ps "git.sericyb.com.au/jotty/permascroll"
"github.com/muesli/termenv"
"github.com/rivo/uniseg"
Expand Down Expand Up @@ -46,8 +47,7 @@ var (
)

const (
cursorCharCap = '↑' // Capitalisation indicator character
errorLabel = "Error: "
cursorCharCap = '↑' // Capitalisation indicator character
margin = 6 // Up to 4 edit marks, cursor and wrap indicator
markChar = '|' // Visual representation of an edit mark
moreChar = '…' // Continuation indicator character
Expand Down Expand Up @@ -166,7 +166,7 @@ func cursorString() string {
}

func errorString() string {
return output.String(string(errorLabel)).Blink().Foreground(output.Color(errorColor)).String()
return output.String(i18n.Text["error"]).Blink().Foreground(output.Color(errorColor)).String()
}

func markString() string {
Expand Down Expand Up @@ -216,8 +216,6 @@ func cutBuffer(cutMax int) (buf string) {

// Draw the status bar that appears on the last line of the screen.
func statusLine() string {
const cutLabel = "cut:"
const helpLabel = "ESC=Help"
const padding = " " // Spaces between items
const separators = 4 // One space after each scope
var c [MaxScope]string // Counters for each scope
Expand Down Expand Up @@ -250,14 +248,14 @@ func statusLine() string {
}
}

if buf := cutBuffer(ex - (w + len(padding) + uniseg.StringWidth(cutLabel) + 4)); buf != "" {
t.WriteString(padding + " " + cutLabel + " " + cutStyle(buf))
w += len(padding) + uniseg.StringWidth(cutLabel) + uniseg.StringWidth(buf) + 2
if buf := cutBuffer(ex - (w + len(padding) + i18n.TextWidth["cut"] + 4)); buf != "" {
t.WriteString(padding + " " + i18n.Text["cut"] + " " + cutStyle(buf))
w += len(padding) + i18n.TextWidth["cut"] + uniseg.StringWidth(buf) + 2
}

// Right-align help label
if align := ex - (w + uniseg.StringWidth(helpLabel) + len(padding)); align > 1 {
t.WriteString(strings.Repeat(" ", align) + helpStyle(helpLabel))
if align := ex - (w + i18n.TextWidth["help"] + len(padding)); align > 1 {
t.WriteString(strings.Repeat(" ", align) + helpStyle(i18n.Text["help"]))
}

return t.String()
Expand Down Expand Up @@ -687,17 +685,16 @@ func Screen() string {
t = append(t, "")
}

if Mode == Help {
window := helpWindow()
t = slices.Delete(t, 0, len(window))
t = slices.Insert(t, 0, window...)
}

switch Mode {
case Quit:
t = append(t, confirmStyle(message))
case Error: // Go error messages always start with a lowercase letter
t = append(t, errorString()+errorStyle(truncate(ex-(uniseg.StringWidth(errorLabel)+1), message)))
case Error:
t = append(t, errorString()+" "+errorStyle(truncate(ex-(i18n.TextWidth["error"]+1), message)))
case Help:
window := helpWindow()
t = slices.Delete(t, 0, len(window))
t = slices.Insert(t, 0, window...)
t = append(t, statusLine())
default:
t = append(t, statusLine())
}
Expand Down
3 changes: 2 additions & 1 deletion edits/edits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"slices"
"testing"

"git.sericyb.com.au/jotty/i18n"
ps "git.sericyb.com.au/jotty/permascroll"
"github.com/muesli/termenv"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -470,7 +471,7 @@ func TestScreen(t *testing.T) {
cursor[Para] = 3
assert.Equal("1 2 \n3 4\n\n_\n@14/14", Screen())

HelpText = []byte("Test")
i18n.HelpText, i18n.HelpWidth = []string{"Test"}, 4
Mode = Help
assert.Equal(" Test\n——————————\n\n_\n@14/14", Screen())

Expand Down
19 changes: 5 additions & 14 deletions edits/help.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package edits

import (
"slices"
"strings"

"git.sericyb.com.au/jotty/i18n"
"github.com/rivo/uniseg"
)

var HelpText []byte

func dropParagraphs(w []string) []string {
i := len(w) - ey
for i < len(w) && len(w[i]) > 0 {
Expand All @@ -24,20 +22,13 @@ func dropParagraphs(w []string) []string {

// The help window.
func helpWindow() (w []string) {
w = strings.Split(string(HelpText), "\n")
if len(w[len(w)-1]) == 0 { // Trim final blank line
w = slices.Delete(w, len(w)-1, len(w))
}

var longest int
for _, l := range w {
longest = max(longest, uniseg.StringWidth(l))
}
w = make([]string, len(i18n.HelpText))
copy(w, i18n.HelpText)

if longest > ex {
if i18n.HelpWidth > ex {
w = rewrap(w)
} else {
padding := strings.Repeat(" ", (ex-longest)/2)
padding := strings.Repeat(" ", (ex-i18n.HelpWidth)/2)
for i, l := range w {
if len(l) > 0 {
w[i] = padding + l
Expand Down
7 changes: 4 additions & 3 deletions edits/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package edits
import (
"testing"

"git.sericyb.com.au/jotty/i18n"
"github.com/stretchr/testify/assert"
)

Expand All @@ -21,13 +22,13 @@ func TestHelpWindow(t *testing.T) {
assert := assert.New(t)
ResizeScreen(6, 8)

HelpText = []byte("")
i18n.HelpText, i18n.HelpWidth = []string{}, 0
assert.Equal([]string{"——————"}, helpWindow())

HelpText = []byte("One\n\nTwo")
i18n.HelpText, i18n.HelpWidth = []string{"One", "", "Two"}, 3
assert.Equal([]string{" One", "", " Two", "——————"}, helpWindow())

HelpText = []byte("Testing\n\nMore\n\nText")
i18n.HelpText, i18n.HelpWidth = []string{"Testing", "", "More", "", "Text"}, 7
assert.Equal([]string{"Testi-", "ng", "", "More", "", "Text", "——————"}, helpWindow())

ResizeScreen(6, 5)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.0
require (
github.com/cespare/xxhash/v2 v2.3.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49
github.com/muesli/termenv v0.15.2
github.com/rivo/uniseg v0.4.7
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8=
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand Down
15 changes: 15 additions & 0 deletions i18n/help.de
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Der Cursor zeigt den aktuellen Bearbeitungs- und Navigationsbereich wie folgt an:
"_"-Zeichen, "#"-Wörter, "$"-Sätze oder "¶"-Absätze.

"|" zeigt bis zu vier Bearbeitungsmarkierungen an, die primäre und sekundäre Auswahlen definieren.
Wenn nur eine Bearbeitungsmarkierung vorhanden ist, fungiert der Cursor als zweite Bearbeitungsmarkierung.
Bei ausgewähltem Text tauscht die Leertaste die Auswahlen aus,
"Eingabetaste"/"Strg-M" und "Entf"/"Strg-X" schneiden die primäre Auswahl aus.

Mit den folgenden Tasten lassen sich spezielle Aktionen ausführen:
"↑" Bereich vergrößern, "↓" Bereich verkleinern, "←" nach links bewegen, "→" nach rechts bewegen,
"Rücktaste"/"Strg-H" vorhergehenden Text löschen, "Eingabetaste"/"Strg-M" neuer Absatz,
"Einfügen"/"Strg-V" ausgeschnittenen Text einfügen, "Entf"/"Strg-X" Text beim Ausschneiden löschen,
"Pos1"/"Strg-U" zum Anfang bewegen, "Ende"/"Strg-D" zum Ende bewegen,
"Tab"/"Strg-I" Markierung setzen, "Umschalt-Tab" alle Markierungen abbrechen, "Strg-C" Text kopieren,
"Strg-Q"/"Strg-W" beenden, "Strg-E" exportieren, "Strg-Z" rückgängig machen, "Strg-Y" wiederherstellen
15 changes: 15 additions & 0 deletions i18n/help.jp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
カーソルは、編集とナビゲーションの現在の範囲を次のように表示します:
「_」文字、「#」単語、「$」文、または「¶」段落。

「|」は、プライマリ選択とセカンダリ選択を定義する最大 4 つの編集マークを示します。
編集マークが 1 つしかない場合、カーソルは 2 番目の編集マークとして機能します。
テキストが選択されているときに「スペース」を押すと選択が交換され、
「Enter」/「Ctrl-M」または「Delete」/「Ctrl-X」を押すと選択が切り取られます。

以下のキーは特別なアクションを実行します:
「↑」 範囲を拡大、「↓」 範囲を縮小、「←」 左に移動、「→」 右に移動、
「Backspace」/「Ctrl-H」 前のテキストを消去、「Enter」/「Ctrl-M」 新しい段落、
「Insert」/「Ctrl-V」 切り取ったテキストを挿入、「Delete」/「Ctrl-X」 切り取ったテキストの削除、
「Home」/「Ctrl-U」 先頭に移動、「End」/「Ctrl-D」 末尾に移動、
「Tab」/「Ctrl-I」 マークを設定、「Shift-Tab」 すべてのマークをクリア、「Ctrl-C」 テキストをコピー、
「Ctrl-Q」/「Ctrl-W」 終了、「Ctrl-E」 エクスポート、「Ctrl-Z」 元に戻す、「Ctrl-Y」 やり直し
54 changes: 54 additions & 0 deletions i18n/i18n.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package i18n

import (
"embed"
"slices"
"strings"

"github.com/jeandeaual/go-locale"
"github.com/rivo/uniseg"
)

//go:embed help.* text.*
var translations embed.FS

var (
HelpText []string
HelpWidth int
Text = make(map[string]string)
TextWidth = make(map[string]int)
)

func init() {
var err error
var userLanguage string
if userLanguage, err = locale.GetLanguage(); err != nil {
userLanguage = "en"
}

var b []byte
if b, err = translations.ReadFile("help." + userLanguage); err != nil {
b, _ = translations.ReadFile("help.en")
}

HelpText = strings.Split(string(b), "\n")
if len(HelpText[len(HelpText)-1]) == 0 { // Trim final blank line
HelpText = slices.Delete(HelpText, len(HelpText)-1, len(HelpText))
}

for _, l := range HelpText {
HelpWidth = max(HelpWidth, uniseg.StringWidth(l))
}

if b, err = translations.ReadFile("text." + userLanguage); err != nil {
b, _ = translations.ReadFile("text.en")
}

s := strings.Split(string(b), "\n")
for _, t := range s {
k, v, _ := strings.Cut(t, "|")
v = strings.Replace(v, `\n`, "\n", -1)
Text[k] = v
TextWidth[k] = uniseg.StringWidth(v)
}
}
6 changes: 6 additions & 0 deletions i18n/text.de
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
confirm|Beenden bestätigen?
cut|Ausschneiden:
error|Fehler:
help|ESC=Hilfe
usage|Verwendung:\n %s [Dateiname]\n\nWenn kein Dateiname angegeben ist, wird standardmäßig „%s“ verwendet\n\nOptionen:
version|Programmversion drucken und beenden
6 changes: 6 additions & 0 deletions i18n/text.en
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
confirm|Confirm exit?
cut|cut:
error|Error:
help|ESC=Help
usage|Usage:\n %s [filename]\n\nIf filename is not provided, defaults to '%s'\n\nOptions:
version|print program version and exit
6 changes: 6 additions & 0 deletions i18n/text.jp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
confirm|終了を確認しますか?
cut|カット:
error|エラー:
help|ESC=ヘルプ
usage|使用方法:\n %s [ファイル名]\n\nファイル名が指定されていない場合は、デフォルトで '%s' になります\n\nオプション:
version|プログラムのバージョンを印刷して終了します
14 changes: 5 additions & 9 deletions jotty.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"embed"
_ "embed"
"flag"
"fmt"
"log"
Expand All @@ -11,13 +11,11 @@ import (
"time"

"git.sericyb.com.au/jotty/edits"
"git.sericyb.com.au/jotty/i18n"
ps "git.sericyb.com.au/jotty/permascroll"
tea "github.com/charmbracelet/bubbletea"
)

//go:embed i18n
var i18n embed.FS

//go:generate sh -c "printf %s $(git describe --always --tags) > version.txt"
//go:embed version.txt
var version string
Expand Down Expand Up @@ -47,12 +45,11 @@ var dispatch = map[tea.KeyType]func(){
var (
exportPath = "jotty.txt"
sx, sy int // screen dimensions
vFlag = flag.Bool("version", false, "print program version and exit")
)

type model struct{ timer *time.Timer }

func confirmExit() { edits.SetMode(edits.Quit, "Confirm exit?") }
func confirmExit() { edits.SetMode(edits.Quit, i18n.Text["confirm"]) }
func export() { edits.Export(exportPath) }
func help() { edits.SetMode(edits.Help, "") }

Expand Down Expand Up @@ -125,13 +122,13 @@ func cleanup() {

func usage() {
fmt.Println("https://github.com/xanni/jotty ⓒ 2024 Andrew Pam <[email protected]>")
fmt.Printf("\nUsage:\n %s [filename]\n\nIf filename is not provided, defaults to '%s'\n\nOptions:\n",
filepath.Base(os.Args[0]), defaultName)
fmt.Printf("\n"+i18n.Text["usage"]+"\n", filepath.Base(os.Args[0]), defaultName)
flag.PrintDefaults()
}

func main() {
flag.Usage = usage
vFlag := flag.Bool("version", false, i18n.Text["version"])
flag.Parse()
if *vFlag {
println(filepath.Base(os.Args[0]) + " " + version)
Expand All @@ -152,7 +149,6 @@ func main() {
}

defer cleanup()
edits.HelpText, _ = i18n.ReadFile("i18n/help.en")

var m model
m.timer = time.AfterFunc(syncDelay, func() {
Expand Down

0 comments on commit c4b4ab1

Please sign in to comment.