Basic API request to DeepL

This commit is contained in:
Markus Scully 2024-12-04 19:44:20 +02:00
parent 3a9f283e99
commit d2892b1f82
12 changed files with 1401 additions and 70 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
.devenv*
devenv.local.nix
.pre-commit-config.yaml
.env

1306
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[workspace]
members = [
"autolingual",
"autolingual-macro"
"endolingual",
"endolingual-macro"
]
resolver = "2"

View file

@ -1,25 +1,22 @@
# Autolingual
# Endolingual
Procedural macros in Rust can execute arbitrary code at compile time, even performing network requests. This has potentially terrifying security implications, but we'll use it to our advantage!
Autolingual is a Rust macro which automatically translates strings of natural language text into multiple foreign languages at once at compile time. The produced translations are `&'static str` values, so they are very lightweight.
Endolingual is a Rust macro which translates strings of natural language text into multiple foreign languages at once by making API requests at compile time. The produced translations are `&'static str` values, so they are very lightweight.
## Examples
```rs
use autolingual::{translate, TranslationSet};
use endolingual::{translate, TranslationSet};
// No context
let button_text_french: &str = translate!("Sign up").fr;
// With context (context will not show up in the final translation)
let button_text_french: &str = translate!("Sign up", "A button for creating a user account.").fr;
// Every supported language at once.
let translation_set: TranslationSet = translate!("Hello!");
let french = translation_set.fr;
let german = translation_set.de;
let portuguese = translation_set.pt;
// ...
```
## Usage
Set the environment variable `DEEPL_API_KEY` to your [DeepL API](https://developers.deepl.com/docs) key. Only DeepL is supported at the moment.
This library is still experimental and in the early stages of development.

View file

@ -1,14 +0,0 @@
[package]
name = "autolingual-macro"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Procedural macros for autolingual"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }

View file

@ -1,18 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
#[proc_macro]
pub fn translate(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let text = input.value();
// TODO: Implement actual translation logic
quote! {
::autolingual::TranslationSet {
en: #text,
fr: "TODO",
}
}.into()
}

View file

@ -1,12 +0,0 @@
pub use autolingual_macro::translate;
pub struct Language {
pub code: &'static str,
pub name: &'static str,
}
#[derive(Debug)]
pub struct TranslationSet {
pub en: &'static str,
pub fr: &'static str,
}

View file

@ -13,10 +13,10 @@ let
};
in
{
# https://devenv.sh/basics/
env.GREET = "devenv";
# https://devenv.sh/packages/
dotenv.enable = true;
packages = [
pkgs-unstable.git
pkgs-unstable.rustup
@ -25,7 +25,6 @@ in
pkgs-unstable.jq
];
# https://devenv.sh/languages/
languages.rust = {
channel = "nightly";
components = [
@ -38,6 +37,7 @@ in
};
enterShell = ''
rustup default nightly
rustup component add clippy
'';

View file

@ -0,0 +1,19 @@
[package]
name = "endolingual-macro"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Procedural macros for endolingual"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
reqwest = { version = "0.12.9", features = ["blocking", "json", "rustls-tls"], default-features = false }
serde_json = "1.0"
[dev-dependencies]
endolingual = { path = "../endolingual", version = "0.1.0" }

View file

@ -0,0 +1,40 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
#[proc_macro]
pub fn translate(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let text = input.value();
let api_key =
std::env::var("DEEPL_API_KEY").expect("Environment variable `DEEPL_API_KEY` must be set");
let client = reqwest::blocking::Client::new();
let response = client
.post("https://api.deepl.com/v2/translate")
.header("Authorization", format!("DeepL-Auth-Key {}", api_key))
.header("Content-Type", "application/json")
.json(&serde_json::json!({
"text": [text],
"target_lang": "FR"
}))
.send()
.expect("Failed to send request to DeepL API");
let translation = response
.json::<serde_json::Value>()
.expect("Failed to parse DeepL API response");
let translated_text = translation["translations"][0]["text"]
.as_str()
.expect("Failed to extract translated text");
quote! {
TranslationSet {
en: #text,
fr: #translated_text,
}
}
.into()
}

View file

@ -1,9 +1,9 @@
[package]
name = "autolingual"
name = "endolingual"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Compile-time natural language translations using translation APIs"
[dependencies]
autolingual-macro = { path = "../autolingual-macro", version = "0.1.0" }
endolingual-macro = { path = "../endolingual-macro", version = "0.1.0" }

24
endolingual/src/lib.rs Normal file
View file

@ -0,0 +1,24 @@
pub use endolingual_macro::translate;
pub struct Language {
pub code: &'static str,
pub name: &'static str,
}
#[derive(Debug)]
pub struct TranslationSet {
pub en: &'static str,
pub fr: &'static str,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_translate() {
let translations = translate!("Hello");
let french: &'static str = translations.fr;
assert_eq!(french, "Bonjour");
}
}