Skip to content

Commit

Permalink
feat: Add formality and basic glossary support
Browse files Browse the repository at this point in the history
  • Loading branch information
cluttrdev committed Dec 17, 2023
1 parent 82b348a commit 62d4bb0
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 44 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Formality option support
- Glossary option support (read-only)

## [0.1.0] - 2023-12-01

Initial release.
Expand Down
86 changes: 85 additions & 1 deletion app.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 22,11 @@ type Application struct {
sourceLang *string
targetLangs []string
targetLang *string

formality string

glossaries []deepl.GlossaryInfo
glossaryIndex int
}

func NewApplication(t *deepl.Translator) (*Application, error) {
Expand All @@ -43,6 48,14 @@ func (app *Application) Run() error {
app.ui.SetFooter(err.Error())
}

if err := app.setFormalityOptions(); err != nil {
app.ui.SetFooter(err.Error())
}

if err := app.setGlossaryOptions(); err != nil {
app.ui.SetFooter(err.Error())
}

app.ui.SetInputTextChangedFunc(func() {
app.input <- app.ui.GetInputText()
})
Expand Down Expand Up @@ -122,7 135,73 @@ func (app *Application) setLanguageOptions() error {
return nil
}

func (app *Application) setFormalityOptions() error {
app.ui.SetFormalityOptions(
[]string{"Automatic", "Formal tone", "Informal tone"},
func(text string, index int) {
switch text {
case "Automatic":
app.formality = ""
case "Formal tone":
app.formality = "prefer_more"
case "Informal tone":
app.formality = "prefer_less"
}
app.updateTranslation()
},
)

return nil
}

func (app *Application) setGlossaryOptions() error {
var opts []string
infos, err := app.translator.ListGlossaries()
if err != nil {
return err
}
app.glossaries = infos
app.glossaryIndex = -1
for _, info := range app.glossaries {
opts = append(opts, info.Name)
}

app.ui.SetGlossaryOptions(opts)
app.ui.SetGlossariesDataFunc(func(text string, index int) (*deepl.GlossaryInfo, []deepl.GlossaryEntry) {
if index == 0 || index > len(app.glossaries) 1 {
return nil, nil
}

info := &app.glossaries[index-1]
if info.Name != text {
return nil, nil
}

entries, err := app.translator.GetGlossaryEntries(info.GlossaryId)
if err != nil {
return nil, nil
}

return info, entries
})
app.ui.SetGlossarySelcetedFunc(func(text string, index int) {
if index > 0 {
name := app.glossaries[index-1].Name
if name != text {
app.glossaryIndex = -1
app.ui.SetFooter(fmt.Sprintf("Glossaries name mismatch: %s != %s", name, text))
return
}
}
app.glossaryIndex = index - 1
app.updateTranslation()
})

return nil
}

func (app *Application) updateTranslation() (err error) {
app.ui.ClearOutputText()
if app.text == "" {
return nil
} else if app.targetLang == nil {
Expand All @@ -136,13 215,18 @@ func (app *Application) updateTranslation() (err error) {
if app.sourceLang != nil && *app.sourceLang != "" {
opts = append(opts, deepl.WithSourceLang(*app.sourceLang))
}
if app.formality != "" {
opts = append(opts, deepl.WithFormality(app.formality))
}
if app.glossaryIndex >= 0 {
opts = append(opts, deepl.WithGlossaryID(app.glossaries[app.glossaryIndex].GlossaryId))
}

translations, err := app.translator.TranslateText(text, targetLang, opts...)
if err != nil {
return err
}

app.ui.ClearOutputText()
for _, translation := range translations {
if err := app.ui.WriteOutputText(strings.NewReader(translation.Text)); err != nil {
return err
Expand Down
51 changes: 51 additions & 0 deletions internal/ui/dialog.go
Original file line number Diff line number Diff line change
@@ -0,0 1,51 @@
package ui

import (
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)

type Dialog struct {
tview.Flex

buttons *tview.Flex
cancel func()
}

func NewDialog(p tview.Primitive) *Dialog {
dialog := &Dialog{
Flex: *tview.NewFlex(),
}
dialog.Flex.SetDirection(tview.FlexRow)

dialog.buttons = tview.NewFlex()

dialog.Flex.
AddItem(p, 0, 1, true).
AddItem(dialog.buttons, 1, 0, false)

dialog.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEsc && dialog.cancel != nil {
dialog.cancel()
return nil
}
return event
})

return dialog
}

func (d *Dialog) AddButton(label string, selected func()) *Dialog {
button := tview.NewButton(label).
SetSelectedFunc(selected)
if d.buttons.GetItemCount() > 0 {
d.buttons.AddItem(tview.NewBox(), 1, 0, false)
}
d.buttons.AddItem(button, 0, 1, false)
return d
}

func (d *Dialog) SetCancelFunc(cancel func()) *Dialog {
d.cancel = cancel
return d
}
6 changes: 5 additions & 1 deletion internal/ui/footer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 2,7 @@ package ui

import (
"errors"
"fmt"

"github.com/gdamore/tcell/v2"
"github.com/mattn/go-shellwords"
Expand Down Expand Up @@ -70,8 71,11 @@ func (ui *UI) setupFooter() {
}

switch args[0] {
case "translate", "glossaries":
case "translate":
ui.switchToPage(args[0])
case "size":
_, _, w, h := ui.translatePage.GetInnerRect()
err = errors.New(fmt.Sprintf("(%d, %d)", w, h))
default:
err = errors.New("invalid command")
}
Expand Down
120 changes: 113 additions & 7 deletions internal/ui/glossaries.go
Original file line number Diff line number Diff line change
@@ -1,19 1,125 @@
package ui

import (
"strings"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"

"github.com/cluttrdev/deepl-go/deepl"
)

type GlossariesDialog struct {
tview.Modal
type GlossariesWidget struct {
tview.Flex

dropDown *tview.DropDown
selected func(string, int)

table *tview.Table
data func(string, int) (*deepl.GlossaryInfo, []deepl.GlossaryEntry)
}

type GlossaryTableContent struct {
tview.TableContentReadOnly

Info *deepl.GlossaryInfo
Entries []deepl.GlossaryEntry
}

func (td *GlossaryTableContent) GetCell(row, column int) *tview.TableCell {
if row < 0 || row > len(td.Entries) || column < 0 || column >= 2 {
return nil
}

cell := tview.NewTableCell("")

if row == 0 {
text := td.Info.SourceLang
if column == 1 {
text = td.Info.TargetLang
}
cell.
SetText(strings.ToUpper(text)).
SetStyle(tcell.StyleDefault.Foreground(tview.Styles.SecondaryTextColor))
} else if column == 0 {
cell.SetText(td.Entries[row-1].Source)
} else if column == 1 {
cell.SetText(td.Entries[row-1].Target)
}

glossariesDropDown *tview.DropDown
return cell.SetExpansion(1)
}

func newGlossariesPage(ui *UI) *GlossariesDialog {
page := &GlossariesDialog{}
func (td *GlossaryTableContent) GetRowCount() int {
if td == nil {
return 0
}
return len(td.Entries) 1
}

func (td *GlossaryTableContent) GetColumnCount() int {
return 2
}

func newGlossariesWidget(ui *UI) *GlossariesWidget {
w := &GlossariesWidget{
Flex: *tview.NewFlex(),
}
// layout
w.Flex.SetDirection(tview.FlexRow)

// dropdown
w.dropDown = tview.NewDropDown().
SetLabel("Select: ")
w.Flex.AddItem(w.dropDown, 1, 0, true)

// table
w.table = tview.NewTable().
SetFixed(1, 2).
SetBorders(true)
w.Flex.AddItem(w.table, 0, 1, true)

return w
}

func (w *GlossariesWidget) GetCurrentOption() (int, string) {
return w.dropDown.GetCurrentOption()
}

func (w *GlossariesWidget) SetOptions(options []string) *GlossariesWidget {
opts := make([]string, 1, len(options) 1)
opts[0] = " "
opts = append(opts, options...)
w.dropDown.SetOptions(opts, w.selectedFunc)
return w
}

func (w *GlossariesWidget) SetDataFunc(data func(string, int) (*deepl.GlossaryInfo, []deepl.GlossaryEntry)) *GlossariesWidget {
w.data = data
return w
}

func (w *GlossariesWidget) SetSelectedFunc(selected func(string, int)) *GlossariesWidget {
w.selected = selected
return w
}

page.glossariesDropDown = tview.NewDropDown()
func (w *GlossariesWidget) selectedFunc(text string, index int) {
var content *GlossaryTableContent = nil
if index > 0 {
info, entries := w.data(text, index)
if info != nil {
content = &GlossaryTableContent{
Info: info,
Entries: entries,
}
}
}
w.table.
SetContent(content).
ScrollToBeginning()

return page
if w.selected != nil {
w.selected(text, index)
}
}
Loading

0 comments on commit 62d4bb0

Please sign in to comment.