├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── _config.yml ├── examples ├── basic.rs ├── builder.rs ├── cli.rs ├── custom_url.rs ├── detect.rs ├── key.rs ├── language.rs ├── method.rs └── unic_langid.rs └── src └── lib.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libretranslate" 3 | version = "0.5.2" 4 | authors = ["Grant Handy ", "Rafael G. Dantas "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "A wrapper for the LibreTranslate web API" 8 | repository = "https://github.com/DefunctLizard/libretranslate-rs/" 9 | documentation = "https://docs.rs/libretranslate" 10 | keywords = ["translate", "language", "libretranslate", "translation"] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | serde_json = "1.0.64" 16 | surf = "2.2.0" 17 | unic-langid = { version = "0.9.0", optional = true } 18 | 19 | [dev-dependencies] 20 | tokio = { version = "1.5.0", features = ["full"]} 21 | 22 | [features] 23 | unicode_langid = ["unic-langid"] 24 | 25 | [[example]] 26 | name = "unic_langid" 27 | required-features = ["unicode_langid"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Grant Handy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libretranslate-rs 2 | [![Crates.io](https://img.shields.io/crates/v/libretranslate.svg)](https://crates.io/crates/libretranslate) 3 | [![Crates.io](https://img.shields.io/crates/d/libretranslate)](https://crates.io/crates/libretranslate) 4 | [![API](https://docs.rs/libretranslate/badge.svg)](https://docs.rs/libretranslate) 5 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/grantshandy/libretranslate-rs) 6 | ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/grantshandy/libretranslate-rs/Rust) 7 | 8 | A LibreTranslate API client for Rust. 9 | ``` 10 | libretranslate = "0.5.1" 11 | ``` 12 | 13 | `libretranslate` allows you to use open source machine translation in your projects through an easy to use API that connects to the official [webpage](https://libretranslate.com/). 14 | 15 | ### Update for Oct, 2021 16 | As of now, [libretranslate.com](https://libretranslate.com) hasn't implemented rate limiting yet, so you must use an alternate instance or have an API key. 17 | 18 | Here are some that work: 19 | - [translate.terraprint.co](https://translate.terraprint.co/) (Free) 20 | - [libretranslate.eownerdead.dedyn.io](https://libretranslate.eownerdead.dedyn.io/) (Free) 21 | - [libretranslate.com](https://libretranslate.com) (Paid) 22 | 23 | You'll need to use a Builder struct or a String method and specify the url to change to an instance that doesn't require an API key. 24 | 25 | ## Basic Example 26 | `libretranslate` is an async library, so you'll have to use an async runtime like [`tokio`](https://crates.io/crates/tokio) or [`async-std`](https://crates.io/crates/async-std). 27 | 28 | All translations are done through the `translate` function: 29 | ```rust 30 | use libretranslate::{translate, Language}; 31 | 32 | #[tokio::main] 33 | async fn main() { 34 | let source = Language::French; 35 | let target = Language::English; 36 | let input = "Le texte français."; 37 | 38 | let data = translate(source, target, input, None).await.unwrap(); 39 | 40 | println!("Input {}: {}", data.source.as_pretty(), data.input); 41 | println!("Output {}: {}", data.target.as_pretty(), data.output); 42 | } 43 | ``` 44 | 45 | Output: 46 | ``` 47 | Input French: le texte français. 48 | Output English: the French text. 49 | ``` 50 | 51 | [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/basic.rs) 52 | 53 | ## Language Detection 54 | Here's a simple example. 55 | ```rust 56 | use libretranslate::{translate, Language}; 57 | 58 | #[tokio::main] 59 | async fn main() { 60 | let target = Language::English; 61 | let text = "le texte français."; 62 | 63 | let data = translate(Language::Detect, target, text, None).await.unwrap(); 64 | 65 | println!("Input {}: {}", data.source.as_pretty(), data.input); 66 | println!("Output {}: {}", data.target.as_pretty(), data.output); 67 | } 68 | ``` 69 | 70 | Output: 71 | ``` 72 | Input French: le texte français. 73 | Output English: the French text. 74 | ``` 75 | 76 | [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/detect.rs) 77 | 78 | ## Language Functionality 79 | The `Language` enum has a lot of functionality so you can create a `Language` from all sorts of different user inputs. 80 | 81 | You can return a `&str` with the language's name in English using `as_pretty()`, or the language's code using `as_code()`. 82 | 83 | `Language` also implements `FromStr` so you can create a `Language` using text like "en", or "English" (case doesn't matter). You can do this by either using `Language::from()` or `.parse::()`. 84 | 85 | Here's a simple example. 86 | ```rust 87 | use libretranslate::Language; 88 | 89 | fn main() { 90 | let lang = Language::English; 91 | let lang_parse = "english".parse::().unwrap(); 92 | 93 | assert_eq!(lang, lang_parse); 94 | assert_eq!("en", lang.as_code()); 95 | assert_eq!("English", lang.as_pretty()); 96 | } 97 | ``` 98 | 99 | [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/language.rs) 100 | 101 | ## String Methods 102 | The trait `Translate` implements [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html), meaning that any `&str` or `String` can be translated into any other language. 103 | 104 | Here's a simple example. 105 | ```rust 106 | use libretranslate::{Language, Translate}; 107 | 108 | #[tokio::main] 109 | async fn main() { 110 | let text = "This is text, written on a computer, in English." 111 | .to_lang(Language::German) 112 | .from_lang(Language::English) 113 | .translate() 114 | .await 115 | .unwrap(); 116 | 117 | println!("output: \"{}\"", text); 118 | } 119 | ``` 120 | 121 | Output: 122 | ``` 123 | Output: "Dies ist Text, geschrieben auf einem Computer, in Englisch." 124 | ``` 125 | 126 | [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/method.rs) 127 | 128 | ## Available Languages 129 | - English 130 | - Arabic 131 | - Chinese 132 | - French 133 | - German 134 | - Italian 135 | - Japanese 136 | - Portuguese 137 | - Russian 138 | - Spanish 139 | - Polish 140 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-dinky -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | // All translations are done through the `translate()` function. 2 | 3 | use libretranslate::{translate, Language}; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let source = Language::French; 8 | let target = Language::English; 9 | let input = "Le texte français."; 10 | 11 | let data = translate(source, target, input, None).await.unwrap(); 12 | 13 | println!("Input {}: {}", data.source.as_pretty(), data.input); 14 | println!("Output {}: {}", data.target.as_pretty(), data.output); 15 | } 16 | -------------------------------------------------------------------------------- /examples/builder.rs: -------------------------------------------------------------------------------- 1 | use libretranslate::{Language, TranslationBuilder}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let builder = TranslationBuilder::new() 6 | .text("le texte francais") 7 | .from_lang(Language::French) 8 | .to_lang(Language::Italian) 9 | .url("https://libretranslate.de/") 10 | // .key("YOUR-OWN-KEY") 11 | .translate() 12 | .await 13 | .unwrap(); 14 | 15 | println!("{}", builder.output); 16 | } 17 | -------------------------------------------------------------------------------- /examples/cli.rs: -------------------------------------------------------------------------------- 1 | // USAGE: 2 | // $ ./args 3 | 4 | // The translated text goes in quotation marks. 5 | 6 | use libretranslate::{translate, Language}; 7 | 8 | #[tokio::main] 9 | async fn main() { 10 | let args = &std::env::args().collect::>(); 11 | 12 | if args.len() != 4 { 13 | eprintln!("FORMAT: "); 14 | std::process::exit(1); 15 | }; 16 | 17 | let source = match args[1].parse::() { 18 | Ok(source) => source, 19 | Err(_) => { 20 | eprintln!("{} is not a valid language.", args[1]); 21 | std::process::exit(1); 22 | } 23 | }; 24 | 25 | let target = match args[2].parse::() { 26 | Ok(source) => source, 27 | Err(_) => { 28 | eprintln!("{} is not a valid language.", args[2]); 29 | std::process::exit(1); 30 | } 31 | }; 32 | 33 | let input = &args[3]; 34 | 35 | match translate(source, target, input, None).await { 36 | Ok(data) => println!("{}", data.output), 37 | Err(error) => println!("Error: {}", error), 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /examples/custom_url.rs: -------------------------------------------------------------------------------- 1 | use libretranslate::{translate_url, Language}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let source = Language::French; 6 | let target = Language::English; 7 | let input = "Le texte français"; 8 | 9 | let data = translate_url(source, target, input, "https://libretranslate.de/", None) 10 | .await 11 | .unwrap(); 12 | 13 | println!("URL: \"{}\"", data.url); 14 | println!("Input {}: \"{}\"", data.source.as_pretty(), data.input); 15 | println!("Output {}: \"{}\"", data.target.as_pretty(), data.output); 16 | } 17 | -------------------------------------------------------------------------------- /examples/detect.rs: -------------------------------------------------------------------------------- 1 | use libretranslate::{translate, Language}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let target = Language::English; 6 | let text = "le texte français."; 7 | 8 | let data = translate(Language::Detect, target, text, None) 9 | .await 10 | .unwrap(); 11 | 12 | println!("Input {}: {}", data.source.as_pretty(), data.input); 13 | println!("Output {}: {}", data.target.as_pretty(), data.output); 14 | } 15 | -------------------------------------------------------------------------------- /examples/key.rs: -------------------------------------------------------------------------------- 1 | use libretranslate::{translate, Language}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let args = &std::env::args().collect::>(); 6 | 7 | let source = Language::English; 8 | let target = Language::Japanese; 9 | let input = "what is the problem!"; 10 | 11 | let data = translate(source, target, input, Some(&args[1])) 12 | .await 13 | .unwrap(); 14 | 15 | println!("Input {}: {}", data.source.as_pretty(), data.input); 16 | println!("Output {}: {}", data.target.as_pretty(), data.output); 17 | } 18 | -------------------------------------------------------------------------------- /examples/language.rs: -------------------------------------------------------------------------------- 1 | // The `Language` enum has a lot of functionality so you can create a `Language` from all sorts of different user inputs. 2 | 3 | // You can return a `&str` with the language's name in English using `as_pretty()`, or the language's code using `as_code()`. 4 | 5 | // `Language` also implements `FromStr` so you can create a `Language` using text like "en", or "English" (case doesn't matter). You can do this by either using `Language::from()` or `.parse::()`. 6 | 7 | use libretranslate::Language; 8 | 9 | fn main() { 10 | let lang = Language::English; 11 | let lang_parse = "english".parse::().unwrap(); 12 | 13 | assert_eq!(lang, lang_parse); 14 | assert_eq!("en", lang.as_code()); 15 | assert_eq!("English", lang.as_pretty()); 16 | } 17 | -------------------------------------------------------------------------------- /examples/method.rs: -------------------------------------------------------------------------------- 1 | // The trait `Translate` implements `AsRef`, meaning that any `&str` or `String` can be translated into any other language. 2 | 3 | use libretranslate::{Language, Translate}; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let text = "This is text, written on a computer, in English." 8 | .to_lang(Language::German) 9 | .from_lang(Language::English) 10 | .url("https://libretranslate.de/") 11 | .translate() 12 | .await 13 | .unwrap(); 14 | 15 | println!("output: \"{}\"", text); 16 | } 17 | -------------------------------------------------------------------------------- /examples/unic_langid.rs: -------------------------------------------------------------------------------- 1 | use libretranslate::{translate_url, Language}; 2 | use unic_langid::LanguageIdentifier; 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | let source = Language::English; 7 | 8 | let li: LanguageIdentifier = "fr-FR".parse().expect("Failed to parse."); 9 | let target: Language = Language::from_unic_langid(li).unwrap(); 10 | 11 | let input = "Amazing!"; 12 | 13 | let data = translate_url(source, target, input, "https://libretranslate.de/", None) 14 | .await 15 | .unwrap(); 16 | 17 | println!("URL: \"{}\"", data.url); 18 | println!("Input {}: \"{}\"", data.source.as_pretty(), data.input); 19 | println!("Output {}: \"{}\"", data.target.as_pretty(), data.output); 20 | } 21 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # libretranslate-rs 2 | //!! [![Crates.io](https:img.shields.io/crates/v/libretranslate.svg)](https://crates.io/crates/libretranslate) 3 | //! [![Crates.io](https://img.shields.io/crates/d/libretranslate)](https://crates.io/crates/libretranslate) 4 | //! [![API](https://docs.rs/libretranslate/badge.svg)](https://docs.rs/libretranslate) 5 | //! [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/grantshandy/libretranslate-rs) 6 | //! ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/grantshandy/libretranslate-rs/Rust) 7 | //! 8 | //! A LibreTranslate API client for Rust. 9 | //! ``` 10 | //! libretranslate = "0.5" 11 | //! ``` 12 | //! 13 | //! `libretranslate` allows you to use open source machine translation in your projects through an easy to use API that connects to the official [webpage](https://libretranslate.com/). 14 | //! 15 | //! ## Basic Example 16 | //! `libretranslate` is an async library, so you'll have to use an async runtime like [`tokio`](https://crates.io/crates/tokio) or [`async-std`](https://crates.io/crates/async-std). 17 | //! 18 | //! All translations are done through the [`translate`](crate::translate) function: 19 | //! ```rust 20 | //! use libretranslate::{translate, Language}; 21 | //! 22 | //! #[tokio::main] 23 | //! async fn main() { 24 | //! let source = Language::French; 25 | //! let target = Language::English; 26 | //! let input = "Le texte français."; 27 | //! 28 | //! let data = translate(source, target, input, None).await.unwrap(); 29 | //! 30 | //! println!("Input {}: {}", data.source.as_pretty(), data.input); 31 | //! println!("Output {}: {}", data.target.as_pretty(), data.output); 32 | //! } 33 | //! ``` 34 | //! 35 | //! Output: 36 | //! ``` 37 | //! Input French: le texte français. 38 | //! Output English: the French text. 39 | //! ``` 40 | //! 41 | //! [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/basic.rs) 42 | //! 43 | //! ## Language Detection 44 | //! Here's a simple example. 45 | //! ```rust 46 | //! use libretranslate::{translate, Language}; 47 | //! 48 | //! #[tokio::main] 49 | //! async fn main() { 50 | //! let target = Language::English; 51 | //! let text = "le texte français."; 52 | //! 53 | //! let data = translate(Language::Detect, target, text, None).await.unwrap(); 54 | //! 55 | //! println!("Input {}: {}", data.source.as_pretty(), data.input); 56 | //! println!("Output {}: {}", data.target.as_pretty(), data.output); 57 | //! } 58 | //! ``` 59 | //! 60 | //! Output: 61 | //! ``` 62 | //! Input French: le texte français. 63 | //! Output English: the French text. 64 | //! ``` 65 | //! 66 | //! [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/detect.rs) 67 | //! 68 | //! ## Language Functionality 69 | //! The `Language` enum has a lot of functionality so you can create a `Language` from all sorts of different user inputs. 70 | //! 71 | //! You can return a `&str` with the language's name in English using `as_pretty()`, or the language's code using `as_code()`. 72 | //! 73 | //! `Language` also implements `FromStr` so you can create a `Language` using text like "en", or "English" (case doesn't matter). You can do this by either using `Language::from()` or `.parse::()`. 74 | //! 75 | //! Here's a simple example. 76 | //! ```rust 77 | //! let lang = Language::English; 78 | //! let lang_parse = "english".parse::().unwrap(); 79 | //! 80 | //! assert_eq!(lang, lang_parse); 81 | //! assert_eq!("en", lang.as_code()); 82 | //! assert_eq!("English", lang.as_pretty()); 83 | //! ``` 84 | //! 85 | //! [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/language.rs) 86 | //! 87 | //! ## String Methods 88 | //! The trait `Translate` implements [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html), meaning that any `&str` or `String` can be translated into any other language. 89 | //! 90 | //! Here's a simple example. 91 | //! ```rust 92 | //! use libretranslate::{Language, Translate}; 93 | //! 94 | //! #[tokio::main] 95 | //! async fn main() { 96 | //! let text = "This is text, written on a computer, in English." 97 | //! .to_lang(Language::German) 98 | //! .from_lang(Language::English) 99 | //! .translate() 100 | //! .await 101 | //! .unwrap(); 102 | //! 103 | //! println!("output: \"{}\"", text); 104 | //! } 105 | //! ``` 106 | //! 107 | //! Output: 108 | //! ``` 109 | //! Output: "Dies ist Text, geschrieben auf einem Computer, in Englisch." 110 | //! ``` 111 | //! 112 | //! [See In Examples Folder](https://github.com/grantshandy/libretranslate-rs/blob/main/examples/method.rs) 113 | //! 114 | //! ## Available Languages 115 | //! - English 116 | //! - Arabic 117 | //! - Chinese 118 | //! - French 119 | //! - German 120 | //! - Italian 121 | //! - Japanese 122 | //! - Portuguese 123 | //! - Russian 124 | //! - Spanish 125 | //! - Polish 126 | //! 127 | 128 | use serde_json::Value; 129 | 130 | /// Data that is output by the [`translate`](translate) function. 131 | #[derive(Debug, Clone, PartialEq, Hash)] 132 | pub struct Translation { 133 | pub url: String, 134 | pub source: Language, 135 | pub target: Language, 136 | pub input: String, 137 | pub output: String, 138 | } 139 | 140 | /// Translate text between two [`Language`](Language). 141 | pub async fn translate>( 142 | source: Language, 143 | target: Language, 144 | input: T, 145 | key: Option, 146 | ) -> Result { 147 | let url = "https://libretranslate.com/"; 148 | 149 | let key: Option = key.map(|data| data.as_ref().to_string()); 150 | 151 | let data = translate_url(source, target, input.as_ref(), url, key).await?; 152 | 153 | Ok(data) 154 | } 155 | 156 | /// Translate using a custom URL. 157 | pub async fn translate_url>( 158 | source: Language, 159 | target: Language, 160 | input: T, 161 | url: T, 162 | key: Option, 163 | ) -> Result { 164 | let complete_url: String; 165 | 166 | if url.as_ref().ends_with('/') { 167 | complete_url = format!("{}translate", url.as_ref()); 168 | } else { 169 | complete_url = format!("{}/translate", url.as_ref()); 170 | }; 171 | 172 | if input.as_ref().chars().count() >= 5000 { 173 | return Err(TranslateError::LengthError); 174 | }; 175 | 176 | let data: Value = match key { 177 | Some(key) => { 178 | serde_json::json!({ 179 | "q": input.as_ref(), 180 | "source": source.as_code(), 181 | "target": target.as_code(), 182 | "api_key": key, 183 | }) 184 | } 185 | None => { 186 | serde_json::json!({ 187 | "q": input.as_ref(), 188 | "source": source.as_code(), 189 | "target": target.as_code(), 190 | }) 191 | } 192 | }; 193 | 194 | let body = match surf::http::Body::from_json(&data) { 195 | Ok(data) => data, 196 | Err(error) => return Err(TranslateError::HttpError(error.to_string())), 197 | }; 198 | 199 | let url = complete_url.clone(); 200 | 201 | let res = match surf::post(complete_url).body(body).recv_string().await { 202 | Ok(data) => data, 203 | Err(error) => return Err(TranslateError::HttpError(error.to_string())), 204 | }; 205 | 206 | let parsed_json: Value = match serde_json::from_str(&res) { 207 | Ok(parsed_json) => parsed_json, 208 | Err(error) => { 209 | return Err(TranslateError::ParseError(error.to_string())); 210 | } 211 | }; 212 | 213 | if let Value::String(error) = &parsed_json["error"] { 214 | return Err(TranslateError::ParseError(error.to_string())); 215 | } 216 | 217 | let output = match &parsed_json["translatedText"] { 218 | Value::String(output) => output, 219 | _ => { 220 | return Err(TranslateError::ParseError(String::from( 221 | "Unable to find translatedText in parsed JSON", 222 | ))) 223 | } 224 | }; 225 | 226 | let input = input.as_ref().to_string(); 227 | let output = output.to_string(); 228 | 229 | Ok(Translation { 230 | url, 231 | source, 232 | target, 233 | input, 234 | output, 235 | }) 236 | } 237 | 238 | use std::str::FromStr; 239 | 240 | /// Languages that can used for input and output of the [`translate`](crate::translate) function. 241 | #[derive(Debug, Clone, PartialEq, Copy, Hash)] 242 | pub enum Language { 243 | Detect, 244 | English, 245 | Arabic, 246 | Chinese, 247 | French, 248 | German, 249 | Italian, 250 | Japanese, 251 | Portuguese, 252 | Russian, 253 | Spanish, 254 | Polish, 255 | } 256 | 257 | impl Language { 258 | /// Return the language with the language code name. (ex. "ar", "de") 259 | pub fn as_code(&self) -> &'static str { 260 | match self { 261 | Language::Detect => "auto", 262 | Language::English => "en", 263 | Language::Arabic => "ar", 264 | Language::Chinese => "zh", 265 | Language::French => "fr", 266 | Language::German => "de", 267 | Language::Italian => "it", 268 | Language::Japanese => "ja", 269 | Language::Portuguese => "pt", 270 | Language::Russian => "ru", 271 | Language::Spanish => "es", 272 | Language::Polish => "pl", 273 | } 274 | } 275 | 276 | /// Return the Language with the full English name. (ex. "Arabic", "German") 277 | pub fn as_pretty(&self) -> &'static str { 278 | match self { 279 | Language::Detect => "Detected", 280 | Language::English => "English", 281 | Language::Arabic => "Arabic", 282 | Language::Chinese => "Chinese", 283 | Language::French => "French", 284 | Language::German => "German", 285 | Language::Italian => "Italian", 286 | Language::Japanese => "Japanese", 287 | Language::Portuguese => "Portuguese", 288 | Language::Russian => "Russian", 289 | Language::Spanish => "Spanish", 290 | Language::Polish => "pl", 291 | } 292 | } 293 | 294 | /// Create a Language from &str like "en" or "French". Case Doesn't matter. 295 | pub fn from>(s: T) -> Result { 296 | return Self::from_str(s.as_ref()); 297 | } 298 | 299 | /// Create a Language from a [`LanguageIdentifier`](unic_langid::LanguageIdentifier). 300 | #[cfg(feature = "unicode_langid")] 301 | pub fn from_unic_langid(s: unic_langid::LanguageIdentifier) -> Result { 302 | match s.language.as_str() { 303 | "en" => Ok(Language::English), 304 | "ar" => Ok(Language::Arabic), 305 | "zh" => Ok(Language::Chinese), 306 | "fr" => Ok(Language::French), 307 | "de" => Ok(Language::German), 308 | "it" => Ok(Language::Italian), 309 | "pt" => Ok(Language::Portuguese), 310 | "ru" => Ok(Language::Russian), 311 | "es" => Ok(Language::Spanish), 312 | "ja" => Ok(Language::Japanese), 313 | "pl" => Ok(Language::Polish), 314 | &_ => Err(LanguageError::FormatError("Unknown Language".to_string())), 315 | } 316 | } 317 | } 318 | 319 | // TODO: Get locale from user to set Language::default(). 320 | impl Default for Language { 321 | fn default() -> Self { 322 | Language::English 323 | } 324 | } 325 | 326 | impl FromStr for Language { 327 | type Err = LanguageError; 328 | 329 | fn from_str(s: &str) -> Result { 330 | match s.to_string().to_lowercase().as_str() { 331 | "en" => Ok(Language::English), 332 | "ar" => Ok(Language::Arabic), 333 | "zh" => Ok(Language::Chinese), 334 | "fr" => Ok(Language::French), 335 | "de" => Ok(Language::German), 336 | "it" => Ok(Language::Italian), 337 | "pt" => Ok(Language::Portuguese), 338 | "ru" => Ok(Language::Russian), 339 | "es" => Ok(Language::Spanish), 340 | "ja" => Ok(Language::Japanese), 341 | "pl" => Ok(Language::Polish), 342 | "english" => Ok(Language::English), 343 | "arabic" => Ok(Language::Arabic), 344 | "chinese" => Ok(Language::Chinese), 345 | "french" => Ok(Language::French), 346 | "german" => Ok(Language::German), 347 | "italian" => Ok(Language::Italian), 348 | "portuguese" => Ok(Language::Portuguese), 349 | "russian" => Ok(Language::Russian), 350 | "spanish" => Ok(Language::Spanish), 351 | "japanese" => Ok(Language::Japanese), 352 | "polish" => Ok(Language::Polish), 353 | "auto" => Ok(Language::Detect), 354 | &_ => Err(LanguageError::FormatError(s.to_string())), 355 | } 356 | } 357 | } 358 | 359 | impl std::fmt::Display for Language { 360 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 361 | match self { 362 | Language::Detect => write!(f, "auto"), 363 | Language::English => write!(f, "en"), 364 | Language::Arabic => write!(f, "ar"), 365 | Language::Chinese => write!(f, "zh"), 366 | Language::French => write!(f, "fr"), 367 | Language::German => write!(f, "de"), 368 | Language::Italian => write!(f, "it"), 369 | Language::Portuguese => write!(f, "pt"), 370 | Language::Russian => write!(f, "ru"), 371 | Language::Spanish => write!(f, "es"), 372 | Language::Japanese => write!(f, "ja"), 373 | Language::Polish => write!(f, "pl"), 374 | } 375 | } 376 | } 377 | 378 | /// Errors that could be outputed by a [`Language`](Language). 379 | #[derive(Debug, Clone, PartialEq, Hash)] 380 | pub enum LanguageError { 381 | FormatError(String), 382 | } 383 | 384 | impl std::error::Error for LanguageError {} 385 | 386 | impl std::fmt::Display for LanguageError { 387 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 388 | match self { 389 | LanguageError::FormatError(error) => { 390 | write!(f, "Unknown Language: {}", error) 391 | } 392 | } 393 | } 394 | } 395 | 396 | /// Errors that could be outputed by [`translate`](crate::translate). 397 | #[derive(Debug, Clone, PartialEq, Hash)] 398 | pub enum TranslateError { 399 | HttpError(String), 400 | ParseError(String), 401 | DetectError, 402 | LengthError, 403 | } 404 | 405 | impl std::error::Error for TranslateError {} 406 | 407 | impl std::fmt::Display for TranslateError { 408 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 409 | match self { 410 | TranslateError::HttpError(error) => { 411 | write!(f, "HTTP request error: {}", error.to_string()) 412 | } 413 | TranslateError::ParseError(error) => { 414 | write!(f, "JSON parsing error: {}", error.to_string()) 415 | } 416 | TranslateError::DetectError => { 417 | write!(f, "Language detection error") 418 | } 419 | TranslateError::LengthError => { 420 | write!(f, "Requested text is too long") 421 | } 422 | } 423 | } 424 | } 425 | 426 | /// A struct created by a [`Translate`](Translate) that can be translated using the translate method. 427 | pub struct Query<'a> { 428 | pub url: &'a str, 429 | pub text: &'a str, 430 | pub source: Language, 431 | pub target: Language, 432 | } 433 | 434 | impl<'a> Query<'a> { 435 | pub fn to_lang(mut self, language: Language) -> Query<'a> { 436 | self.target = language; 437 | self 438 | } 439 | 440 | pub fn from_lang(mut self, language: Language) -> Query<'a> { 441 | self.source = language; 442 | self 443 | } 444 | 445 | pub fn url(mut self, url: &'a str) -> Query { 446 | self.url = url; 447 | self 448 | } 449 | 450 | pub async fn translate(self) -> Result { 451 | let res = crate::translate_url(self.source, self.target, self.text, self.url, None).await?; 452 | Ok(res.output) 453 | } 454 | } 455 | 456 | /// Translate text from a [`String`](std::string::String) or [`str`](std::str) (anything that implements [`AsRef`](std::convert::AsRef)). 457 | pub trait Translate { 458 | fn to_lang(&self, language: Language) -> Query; 459 | fn from_lang(&self, language: Language) -> Query; 460 | } 461 | 462 | impl Translate for T 463 | where 464 | T: AsRef, 465 | { 466 | fn to_lang(&self, language: Language) -> Query { 467 | Query { 468 | url: "https://libretranslate.com/", 469 | text: self.as_ref(), 470 | source: Language::Detect, 471 | target: language, 472 | } 473 | } 474 | 475 | fn from_lang(&self, language: Language) -> Query { 476 | Query { 477 | url: "https://libretranslate.com/", 478 | text: self.as_ref(), 479 | source: language, 480 | target: Language::default(), 481 | } 482 | } 483 | } 484 | 485 | /// Build Translations more verbosely. 486 | #[derive(Debug, Clone, PartialEq, Hash)] 487 | pub struct TranslationBuilder { 488 | pub url: String, 489 | pub source: Language, 490 | pub target: Language, 491 | pub input: String, 492 | key: Option, 493 | } 494 | 495 | impl TranslationBuilder { 496 | pub fn new() -> Self { 497 | Self { 498 | url: String::from("https://libretranslate.com/"), 499 | source: Language::Detect, 500 | target: Language::default(), 501 | input: String::new(), 502 | key: None, 503 | } 504 | } 505 | 506 | pub fn url>(mut self, url: T) -> Self { 507 | self.url = url.as_ref().to_string(); 508 | self 509 | } 510 | 511 | pub fn from_lang(mut self, lang: Language) -> Self { 512 | self.source = lang; 513 | self 514 | } 515 | 516 | pub fn to_lang(mut self, lang: Language) -> Self { 517 | self.target = lang; 518 | self 519 | } 520 | 521 | pub fn text>(mut self, text: T) -> Self { 522 | self.input = text.as_ref().to_string(); 523 | self 524 | } 525 | 526 | pub fn key>(mut self, key: T) -> Self { 527 | self.key = Some(key.as_ref().to_string()); 528 | self 529 | } 530 | 531 | pub async fn translate(mut self) -> Result { 532 | if self.input.is_empty() { 533 | return Ok(Translation { 534 | url: self.url, 535 | source: self.source, 536 | target: self.target, 537 | input: self.input, 538 | output: String::new(), 539 | }); 540 | }; 541 | 542 | let data = translate_url( 543 | self.source, 544 | self.target, 545 | self.input.clone(), 546 | self.url.clone(), 547 | self.key, 548 | ) 549 | .await?; 550 | 551 | self.source = data.source; 552 | self.target = data.target; 553 | 554 | Ok(Translation { 555 | url: self.url, 556 | source: self.source, 557 | target: self.target, 558 | input: self.input, 559 | output: data.output, 560 | }) 561 | } 562 | } 563 | impl Default for TranslationBuilder { 564 | fn default() -> Self { 565 | Self::new() 566 | } 567 | } 568 | --------------------------------------------------------------------------------