Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added gd-translate command #28

Merged
merged 12 commits into from
Jan 10, 2025
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,26 @@ This script searches Anki cards in your collection that contain %GDWORD%.
gd-ankisearch --field-name VocabKanji --show-fields VocabKanji,SentKanji,Image,SentAudio --word %GDWORD%
```

## gd-translate

**Usage**

```
gd-translate --sentence %GDSEARCH%
gd-translate --spoiler yes --to fr --sentence %GDSEARCH%
```

**Dependencies**

This script requires [Argos Translate](https://github.com/argosopentech/argos-translate) and the JA -> target language package to be installed.

```sh
pip install argostranslate
argospm install translate-ja_en

argos-translate -f ja --t en "Hello World!"
```

## gd-mandarin

This script passes a sentence through mecab in order to make every part of the sentence clickable.
Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "massif.h"
#include "mecab_split.h"
#include "precompiled.h"
#include "translate.h"
#include "util.h"

static constexpr std::string_view help_text = R"EOF(usage: {} ACTION [OPTIONS]
Expand All @@ -32,6 +33,7 @@ ACTIONS
ankisearch Search word in Anki.
massif Search word on Massif.
images Search images on Bing.
translate Translate text using argostranslate.
marisa Split search string using MARISA.
mecab Split search string using Mecab.
strokeorder Show stroke order of a word.
Expand Down Expand Up @@ -92,6 +94,8 @@ auto take_action(std::span<std::string_view const> const args) -> void
return massif(rest);
case "gd-images"_h:
return images(rest);
case "gd-translate"_h:
return translate(rest);
case "gd-marisa"_h:
return marisa_split(rest);
case "gd-mecab"_h:
Expand All @@ -114,6 +118,8 @@ auto take_action(std::span<std::string_view const> const args) -> void
return massif(rest);
case "images"_h:
return images(rest);
case "translate"_h:
return translate(rest);
case "marisa"_h:
return marisa_split(rest);
case "mecab"_h:
Expand Down
2 changes: 1 addition & 1 deletion src/precompiled.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

// Other
#include <cpr/cpr.h>

#include <subprocess.hpp>
#include <marisa/trie.h>
#include <mecab.h>
#include <nlohmann/json.hpp>
Expand Down
105 changes: 105 additions & 0 deletions src/translate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* gd-tools - a set of programs to enhance goldendict for immersion learning.
* Copyright (C) 2025 Ajatt-Tools
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "precompiled.h"
#include "util.h"

using namespace std::literals;
namespace sp = subprocess;

static constexpr std::string default_to{ "en" };
static constexpr std::string_view help_text = R"EOF(usage: gd-translate [OPTIONS]

Translate text from Japanese to target language

OPTIONS
--to LANG target language (default: en)
--sentence SENTENCE japanese sentence to translate
--spoiler yes/no black out the sentence with a spoiler box (default: no)

EXAMPLES
gd-translate --spoiler yes --sentence %GDSEARCH%
gd-translate --spoiler yes --to fr --sentence %GDSEARCH%
)EOF";
static constexpr std::string_view css_style = R"EOF(<style>
.spoiler {
background-color: black;
padding: 6px;
width: fit-content;
}
.spoiler:hover {
background-color: white;
}
</style>)EOF";

struct translate_params
{
std::string to{ default_to };
std::string gd_word;
std::string spoiler{ "no" };

void assign(std::string_view const key, std::string_view const value)
{
if (key == "--to") {
to = value;
} else if (key == "--sentence") {
gd_word = value;
} else if (key == "--spoiler") {
if (value == "yes") {
spoiler = value;
}
}
}
};

void exec_translate(translate_params const& params)
{
auto cmd_argos = sp::Popen(
{
"argos-translate",
"-f",
"ja",
"-t",
params.to,
params.gd_word,
},
sp::output{ sp::PIPE }
);

auto cmd_tail = sp::Popen({ "tail", "-n1" }, sp::input{ cmd_argos.output() }, sp::output{ sp::PIPE });

auto resp = cmd_tail.communicate().first;

std::println("<div{}>", params.spoiler == "yes" ? " class=\"spoiler\"" : "");
std::println("{}", std::string_view(resp.buf.data(), resp.length));
std::println("</div>");
std::println("{}", css_style);
}

void translate(std::span<std::string_view const> const args)
{
try {
exec_translate(fill_args<translate_params>(args));
} catch (gd::help_requested const& ex) {
std::print(help_text);
} catch (gd::runtime_error const& ex) {
std::println("{}", ex.what());
} catch (std::runtime_error const& ex) {
std::println("subprocess error. {}", ex.what());
}
}
5 changes: 5 additions & 0 deletions src/translate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include "precompiled.h"

void translate(std::span<std::string_view const> const args);
35 changes: 33 additions & 2 deletions xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_rules("mode.debug", "mode.release")
add_rules("plugin.compile_commands.autoupdate", {outputdir = "build"})

add_requires("cpr >= 1.11", {configs = {ssl = true}})
add_requires("cpp-subprocess")
add_requires("nlohmann_json", "marisa", "rdricpp", "mecab")

if is_mode("debug") then
Expand Down Expand Up @@ -61,7 +62,7 @@ end
-- Main target
target(main_bin_name)
set_kind("binary")
add_packages("cpr","nlohmann_json", "marisa", "rdricpp", "mecab")
add_packages("cpr","nlohmann_json", "marisa", "rdricpp", "mecab", "cpp-subprocess")
add_files("src/*.cpp")
add_cxflags("-D_GLIBCXX_ASSERTIONS")
set_pcxxheader("src/precompiled.h")
Expand Down Expand Up @@ -130,7 +131,7 @@ if has_config("tests") then
-- Tests target
target("tests")
set_kind("binary")
add_packages("cpr", "nlohmann_json", "marisa", "catch2", "rdricpp", "mecab")
add_packages("cpr", "nlohmann_json", "marisa", "catch2", "rdricpp", "mecab", "cpp-subprocess")
add_files("src/*.cpp", "tests/*.cpp")
remove_files("src/main.cpp")
set_pcxxheader("src/precompiled.h")
Expand Down Expand Up @@ -160,3 +161,33 @@ package("rdricpp")
import("package.tools.xmake").install(package)
end)
package_end()

package("cpp-subprocess")
set_kind("library", {headeronly = true})
set_homepage("https://github.com/arun11299/cpp-subprocess")
set_description("Subprocessing with modern C++.")
set_license("MIT")

set_urls("https://github.com/arun11299/cpp-subprocess.git")
add_versions("2024.01.25", "4025693decacaceb9420efedbf4967a04cb028e7")

add_links("cpp-subprocess")

on_install(function (package)
os.cp("subprocess.hpp", package:installdir("include"))
end)

on_test(function (package)
assert(package:check_cxxsnippets({test = [[
#include <subprocess.hpp>
#include <cstring>
#include <thread>
namespace sp = subprocess;
int main() {
auto obuf = sp::check_output({"ls", "-l"});
std::cout << "Data : " << obuf.buf.data() << std::endl;
std::cout << "Data len: " << obuf.length << std::endl;
}
]]}, {configs = {languages = "c++23"}}))
end)
package_end()