Compare commits

..

34 Commits
v1.0.0 ... main

Author SHA1 Message Date
ElBe
21d4ba85e9
Merge pull request #5 from ElBe-Development/dependabot/github_actions/actions/setup-python-5
Bump actions/setup-python from 4 to 5
2025-01-24 12:53:05 +01:00
ElBe
1addf7b72a
Merge pull request #11 from ElBe-Development/dependabot/pip/pre-commit-4.1.0
Bump pre-commit from 3.5.0 to 4.1.0
2025-01-23 21:28:44 +01:00
ElBe
7251484eaa
Merge pull request #7 from ElBe-Development/dependabot/github_actions/actions/dependency-review-action-4
Bump actions/dependency-review-action from 3 to 4
2025-01-23 21:26:25 +01:00
dependabot[bot]
1a749cb6ab
Bump pre-commit from 3.5.0 to 4.1.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.5.0 to 4.1.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.5.0...v4.1.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-23 20:26:23 +00:00
ElBe
1ac8dcf6a5
Merge pull request #8 from ElBe-Development/dependabot/github_actions/codecov/codecov-action-4
Bump codecov/codecov-action from 3 to 4
2025-01-23 21:26:03 +01:00
ElBe
3d649300c7
Merge pull request #4 from ElBe-Development/dependabot/github_actions/actions/labeler-5
Bump actions/labeler from 4 to 5
2025-01-23 21:24:35 +01:00
dependabot[bot]
b9a8a54232
Bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 13:04:46 +00:00
dependabot[bot]
a79508348c
Bump actions/dependency-review-action from 3 to 4
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3 to 4.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 12:33:50 +00:00
dependabot[bot]
152f973edd
Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-06 12:54:42 +00:00
dependabot[bot]
39228823bd
Bump actions/labeler from 4 to 5
Bumps [actions/labeler](https://github.com/actions/labeler) from 4 to 5.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 12:41:40 +00:00
ElBe
57bb936433
Bump pre-commit from 3.4.0 to 3.5.0
Bump pre-commit from 3.4.0 to 3.5.0
2023-10-23 20:18:53 +02:00
dependabot[bot]
f2caa5c3b1
Bump pre-commit from 3.4.0 to 3.5.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 12:44:34 +00:00
ElBe
163e31bb45 Added t!() macro 2023-10-06 07:39:56 +02:00
ElBe
a8fd02158b Fixed yaml syntax error 2023-09-15 15:13:42 +02:00
ElBe
a85b394b1e Added one single missing bang 2023-09-15 15:10:31 +02:00
ElBe
21f8c781ee Fixed license field 2023-09-15 07:23:25 +02:00
ElBe
3f69ad6aaf Bump version 2023-09-15 07:19:36 +02:00
ElBe
e011a01b31 Bump pre-commit from 3.3.2 to 3.4.0 2023-09-15 07:18:17 +02:00
ElBe
70f5e4b28b Bump actions/checkout from 3 to 4 2023-09-15 07:17:22 +02:00
ElBe
5f1952f362 Fixed license and bump dependencies 2023-09-15 07:14:05 +02:00
ElBe
97b67a7713 Renamed error import 2023-09-15 07:13:25 +02:00
ElBe
31a390f25f Fixed tests being run 2023-09-15 07:13:05 +02:00
ElBe
25ecd37d7d Added codecov 2023-09-15 07:12:39 +02:00
ElBe
195b9edc34 Fixed indent 2023-09-14 19:40:18 +02:00
ElBe
f50157eae0 Updated justfile 2023-09-14 19:38:03 +02:00
ElBe
4e8f910351 Added struct field documentation 2023-09-14 19:33:36 +02:00
ElBe
9e0a4d087b Bump version 2023-09-14 19:17:01 +02:00
ElBe
e18a62267f Formatted files 2023-09-14 19:15:40 +02:00
ElBe
6fd9e66ce8 Implemented errors 2023-09-14 19:14:34 +02:00
ElBe
53682cf1b7 Added errors 2023-09-14 18:34:47 +02:00
ElBe
5db6014654 Updated documentation 2023-09-14 17:17:48 +02:00
ElBe
6e7b2e9cc5 Fixed badge 2023-09-14 17:16:46 +02:00
ElBe
2b76f50d4c Fixed spelling mistake 2023-09-14 17:14:54 +02:00
ElBe
66363879c8 Converted tabs to spaces and fixed tests 2023-09-14 17:08:58 +02:00
18 changed files with 633 additions and 143 deletions

View File

@ -22,7 +22,7 @@ and a pull request. See below for making changes on your own.
1. Create a new issue with a summary of your proposed changes and more.
2. Fork the repository.
3. Implement your changes
4. Test your changes. Run `cargo fmt`, `cargo check` ad `cargo clippy` to verify it works.
4. Test your changes. Run `cargo fmt`, `cargo check` and `cargo clippy` to verify it works.
5. Create a pull request from your fork.
6. If it gets accepted, your changes will be in the next release.

42
.github/README.md vendored
View File

@ -1,11 +1,11 @@
<h1 align="center">
localizer-rs
localizer-rs
</h1>
<h3 align="center">
Localizer helps localize (translate) your rust applications using json files.
</h3>
<p align="center">
<img src="https://img.shields.io/crates/v/qstash-rs">
<img src="https://img.shields.io/crates/v/localizer-rs">
<img src="https://www.codefactor.io/repository/github/ElBe-Development/localizer-rs/badge">
<img src="https://github.com/ElBe-Development/localizer-rs/actions/workflows/megalinter.yml/badge.svg?branch=main&event=push">
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit">
@ -38,60 +38,48 @@ To use localizer-rs, you need a directory (eg. `translations`) with your transla
1. Import the localizer-rs crate:
```rust
use localizer_rs;
```
```rust,ignore
use localizer_rs;
```
2. Create a new config object:
```rust
let config = localizer_rs::Config::new("DIRECTORY NAME", "LANGUAGE NAME");
```
```rust,ignore
let config = localizer_rs::Config::new("translations", "en");
```
3. Translate your text:
```rust
config.t("key", vec!["placeholder", "value"]);
```
```rust,ignore
localizer_rs::t!(config, "key", "placeholder" ="value");
```
## Example
With the following `en.json` file.
```json
{
"error": "{{color.red}}{{bold}}Error:{{end}} Something went wrong: {{details}}."
"error": "{{color.red}}{{bold}}Error:{{end}} Something went wrong: {{details}}."
}
```
And the following rust code.
```rust
```rust,ignore
use localizer_rs;
fn main() {
let config: localizer_rs::Config = localizer_rs::Config::new("translations", "en");
let config: localizer_rs::Config = localizer_rs::Config::new("translations", "en");
println!("{:}", config.t("error", vec![("details", "Path not found")]));
println!("{:}", localizer_rs::t!(config, "error", "details" = "Path not found"));
}
```
You will get the following output:
```bash
Error: Something went wrong: Path not found.
```
Where `Error:` is red and bold.

3
.github/SECURITY.md vendored
View File

@ -5,6 +5,9 @@
| Version | Supported |
| -------- | ------------------ |
| `v1.0.0` | :white_check_mark: |
| `v1.1.0` | :white_check_mark: |
| `v1.1.1` | :white_check_mark: |
| `v1.2.0` | :white_check_mark: |
## Reporting a Vulnerability

3
.github/errors.md vendored Normal file
View File

@ -0,0 +1,3 @@
# errors module
Module for dealing with errors.

14
.github/workflows/codecov_workflow.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Codecov
permissions: read-all
on:
[push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@ -16,9 +16,9 @@ jobs:
matrix:
python-version: ["3.8"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies from txt files
@ -28,7 +28,7 @@ jobs:
install-rust-dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install dependencies from Cargo.toml
run: cargo update
- name: Install dev-dependencies from Cargo.toml
@ -39,8 +39,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
if: ${{ github.event_name != 'push' }}
- name: Dependency Review
if: ${{ github.event_name != 'push' }}
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v4

View File

@ -20,6 +20,6 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@v4
- uses: actions/labeler@v5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -26,7 +26,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
fetch-depth: 0

View File

@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build
run: cargo build --verbose
- name: Run tests

View File

@ -1,7 +1,7 @@
[package]
name = "localizer-rs"
description = "Localizer helps localize (translate) your rust applications using json files."
version = "1.0.0"
version = "1.2.0"
authors = [
"ElBe-Plaq <elbe.dev.plaq@gmail.com>"
]
@ -10,7 +10,7 @@ rust-version = "1.69"
documentation = "https://docs.rs/localizer_rs/"
readme = ".github/README.md"
repository = "https://github.com/ElBe-Development/localizer-rs/"
license-file = "LICENSE.txt"
license = "MIT"
keywords = ["i18n", "L10n", "json", "local", "translation"]
categories = [
"internationalization",
@ -20,4 +20,4 @@ publish = true
[dependencies]
serde = "1.0.188"
serde_json = "1.0.106"
serde_json = "1.0.107"

10
codecov.yml Normal file
View File

@ -0,0 +1,10 @@
coverage:
status:
project:
default:
target: 50%
threshold: 1%
comment:
require_changes: true
require_ci_to_pass: false

View File

@ -1 +1 @@
pre-commit==3.3.2; python_version>='3.8'
pre-commit==4.1.0; python_version>='3.8'

View File

@ -1,10 +1,20 @@
use localizer_rs;
fn main() {
let config: localizer_rs::Config = localizer_rs::Config::new("translations", "en");
let config: localizer_rs::Config = localizer_rs::Config::new("translations", "en");
println!("{:}", config.t("error", vec![("details", "Something went wrong when trying to do stuff")]));
println!("{:}", config.t("success", vec![("balance", "$10"), ("user", "John Doe")]));
println!(
"{:}",
localizer_rs::t!(
config,
"error",
"details" = "Something went wrong when trying to do stuff"
)
);
println!(
"{:}",
localizer_rs::t!(config, "success", "balance" = "$10", "user" = "John Doe")
);
println!("{:}", config.t("all", vec![]));
println!("{:}", localizer_rs::t!(config, "all"));
}

View File

@ -10,8 +10,7 @@ build *ARGUMENTS:
# Removes temporary files
clean:
rm -rf target
rm -rf Tools/__pycache__
cargo clean
# Lints the rust source files
lint *ARGUMENTS:

159
src/errors.rs Normal file
View File

@ -0,0 +1,159 @@
#![doc = include_str!("../.github/errors.md")]
// localizer-rs errors
// Version: 1.1.1
// Copyright (c) 2023-present ElBe Development.
// 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:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 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.
////////////////////////////////
// IMPORTS AND USE STATEMENTS //
////////////////////////////////
use std::fmt;
///////////
// ERROR //
///////////
/// Error object.
///
/// Use [`Error::new()`] to create error objects instead of using this struct.
///
/// # Parameters
///
/// - `name`: The errors name.
/// - `description`: The error description.
/// - `exit_code`: The errors exit code.
///
/// # Returns
///
/// A new `Error` object with the specified name and description.
///
/// # Examples
///
/// ```rust
/// # use localizer_rs;
/// localizer_rs::errors::Error {
/// name: "name".to_owned(),
/// description: "description".to_owned(),
/// exit_code: 1
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Error {
/// The errors name.
pub name: String,
/// The error description.
pub description: String,
/// The errors exit code.
pub exit_code: i32,
}
/// Display implementation for the error object.
impl fmt::Display for Error {
/// Format implementation for the error object.
///
/// # Parameters
///
/// - `self`: The error object.
/// - `f`: The [`fmt::Formatter`] to use.
///
/// # Returns
///
/// A [`fmt::Result`] containing the formatted error message.
///
/// # Examples
///
/// ```rust
/// # use localizer_rs;
/// # let error = localizer_rs::errors::Error::new("name", "description", 1);
/// println!("{}", error);
/// ```
///
/// # See also
///
/// - [`fmt::Display`]
/// - [`Error`]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\x1b[31;1m{}\x1b[0m: {}", self.name, self.description)
}
}
impl Error {
/// Creates a new error object.
///
/// # Parameters
///
/// - `name`: The errors name.
/// - `description`: The error description.
/// - `exit_code`: The errors exit code.
///
/// # Returns
///
/// A new `Error` object with the specified name and description.
///
/// # Examples
///
/// ```rust
/// # use localizer_rs;
/// localizer_rs::errors::Error::new("name", "description", 1);
/// ```
///
/// # See also
///
/// - [`Error`]
pub fn new(name: &str, description: &str, exit_code: i32) -> Error {
return Error {
name: name.to_owned(),
description: description.to_owned(),
exit_code: exit_code,
};
}
/// Raises the error and exits with the specified exit code.
///
/// # Parameters
///
/// - `self`: The error object.
/// - `details`: The error details.
///
/// # Aborts
///
/// Exits with the specified exit code.
///
/// # Examples
///
/// ```should_panic
/// # use localizer_rs;
/// # let error: localizer_rs::errors::Error = localizer_rs::errors::Error::new("name", "description", 1);
/// error.raise("Something went very wrong");
/// ```
///
/// # See also
///
/// - [`Error`]
pub fn raise(&self, details: &str) {
eprintln!("{}", self);
eprintln!("{}", details);
std::process::exit(self.exit_code);
}
}

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../.github/README.md")]
// localizer-rs
// Version: 1.0.0
// Version: 1.2.0
// Copyright (c) 2023-present ElBe Development.
@ -21,6 +22,13 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/////////////
// EXPORTS //
/////////////
pub mod errors;
////////////////////////////////
// IMPORTS AND USE STATEMENTS //
////////////////////////////////
@ -28,7 +36,6 @@
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::process::exit;
use serde_json;
@ -43,7 +50,8 @@ use serde_json;
///
/// # Parameters
///
/// - `path`: The directory containing the translation files. The directory is relative to the path the executable was executed from.
/// - `path`: The directory containing the translation files.
/// The directory is relative to the path the executable was executed from.
/// - `language`: The language to translate to.
///
/// # Returns
@ -61,8 +69,11 @@ use serde_json;
/// ```
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Config {
pub path: String,
pub language: String
/// The directory containing the translation files. The directory is relative to the path the
/// executable was executed from.
pub path: String,
/// The language to translate to.
pub language: String,
}
@ -75,7 +86,8 @@ impl Config {
///
/// # Parameters
///
/// - `path`: The directory containing the translation files. The directory is relative to the path the executable was executed from.
/// - `path`: The directory containing the translation files.
/// The directory is relative to the path the executable was executed from.
/// - `language`: The language to translate to.
///
/// # Returns
@ -96,23 +108,25 @@ impl Config {
/// # See also
///
/// - [`Config`]
pub fn new(path: &str, language: &str) -> Config {
let mut config: Config = Config {
path: "".to_string(),
language: "".to_string()
}.to_owned();
config = config.set_language(language).to_owned();
config = config.set_path(path).to_owned();
pub fn new(path: &str, language: &str) -> Config {
let mut config: Config = Config {
path: "".to_string(),
language: "".to_string(),
}
.to_owned();
config = config.set_language(language).to_owned();
config = config.set_path(path).to_owned();
return config;
}
return config;
}
/// Sets the path for the config object.
///
/// # Parameters
///
/// - `self`: The config object.
/// - `str_path`: The directory containing the translation files. The directory is relative to the path the executable was executed from.
/// - `self`: The config object. This must be mutable.
/// - `str_path`: The directory containing the translation files.
/// The directory is relative to the path the executable was executed from.
///
/// # Returns
///
@ -126,38 +140,47 @@ impl Config {
///
/// ```rust
/// # use localizer_rs;
/// # let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "language");
/// # let mut config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "language");
/// config.set_path("examples");
/// ```
///
/// # See also
///
/// - [`Config`]
pub fn set_path(&mut self, str_path: &str) -> &Config {
let path: &Path = Path::new(str_path);
pub fn set_path(&mut self, str_path: &str) -> &Config {
let path: &Path = Path::new(str_path);
match path.try_exists() {
Ok(value) => {
if !value {
eprintln!("Translation path {:?} does not exist", str_path);
exit(1);
}
},
Err(error) => {
eprintln!("Can't open translation path {:?}: {}", str_path, error);
exit(2);
}
}
match path.try_exists() {
Ok(value) => {
if !value {
let error: errors::Error =
errors::Error::new("OS Error", "Translation path was not found", 1);
error.raise(format!("Path: {:?}", str_path).as_str());
}
}
Err(_error) => {
let error: errors::Error = errors::Error::new("OS Error", "Could not open path", 2);
error.raise(format!("Path: {:?}\nDetails: {}", str_path, _error).as_str());
}
}
self.path = String::from(path.to_owned().to_str().expect("Expected valid path"));
return self;
}
self.path = String::from(match path.to_owned().to_str() {
Some(value) => value,
None => {
let error: errors::Error =
errors::Error::new("OS Error", "Path does not seem to be valid", 3);
error.raise(format!("Path: {:?}", str_path).as_str());
""
}
});
return self;
}
/// Sets the language for the config object.
///
/// # Parameters
///
/// - `self`: The config object.
/// - `self`: The config object. This must be mutable.
/// - `language`: The language to translate to.
///
/// # Returns
@ -168,17 +191,17 @@ impl Config {
///
/// ```rust
/// # use localizer_rs;
/// # let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "language");
/// # let mut config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "language");
/// config.set_language("en");
/// ```
///
/// # See also
///
/// - [`Config`]
pub fn set_language(&mut self, language: &str) -> &Config {
self.language = language.to_string();
return self;
}
pub fn set_language(&mut self, language: &str) -> &Config {
self.language = language.to_string();
return self;
}
/// Translates the specified key in the language specified in the config.
///
@ -202,52 +225,224 @@ impl Config {
///
/// # See also
///
/// - [`t!()`]
/// - [`Config`]
pub fn t(&self, key: &str, arguments: Vec<(&str, &str)>) -> String {
return self.translate::<serde_json::Value>(key, arguments);
}
pub fn t(&self, key: &str, arguments: Vec<(&str, &str)>) -> String {
return self.translate(key, arguments);
}
fn translate<T>(&self, key: &str, mut arguments: Vec<(&str, &str)>) -> String
where
T: serde::Serialize + for<'de> serde::Deserialize<'de>
{
let mut colors: Vec<(&str, &str)> = vec![
// Formatting codes
("end", "\x1b[0m"), ("bold", "\x1b[1m"), ("italic", "\x1b[3m"), ("underline", "\x1b[4m"), ("overline", "\x1b[53m"),
/// Translates the specified key in the language specified in the config.
///
/// # Parameters
///
/// - `self`: The config object.
/// - `key`: The key to translate to.
/// - `arguments`: The arguments to replace.
///
/// # Returns
///
/// A `String` containing the translated value.
///
/// # Raises
///
/// This method throws an exception and exits if
///
/// - The translation file could not be found
/// - The translation file could not be opened
/// - The translation file could not be parsed
/// - The parsed json could not be converted to a json value
/// - The converted json could not be indexed
///
/// # Examples
///
/// ```rust
/// # use localizer_rs;
/// # let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
/// config.translate("test", vec![]);
/// ```
///
/// # See also
///
/// - [`t!()`]
/// - [`Config`]
/// - [`Config::t()`]
/// - [`serde_json`]
pub fn translate(&self, key: &str, mut arguments: Vec<(&str, &str)>) -> String {
let mut colors: Vec<(&str, &str)> = vec![
// Formatting codes
("end", "\x1b[0m"),
("bold", "\x1b[1m"),
("italic", "\x1b[3m"),
("underline", "\x1b[4m"),
("overline", "\x1b[53m"),
// Foreground colors
("color.black", "\x1b[30m"), ("color.red", "\x1b[31m"), ("color.green", "\x1b[32m"), ("color.yellow", "\x1b[33m"),
("color.blue", "\x1b[34m"), ("color.magenta", "\x1b[35m"), ("color.cyan", "\x1b[36m"), ("color.white", "\x1b[37m"),
// Foreground colors
("color.black", "\x1b[30m"),
("color.red", "\x1b[31m"),
("color.green", "\x1b[32m"),
("color.yellow", "\x1b[33m"),
("color.blue", "\x1b[34m"),
("color.magenta", "\x1b[35m"),
("color.cyan", "\x1b[36m"),
("color.white", "\x1b[37m"),
// Bright foreground colors
("color.bright_black", "\x1b[90m"), ("color.bright_red", "\x1b[91m"), ("color.bright_green", "\x1b[92m"),
("color.bright_yellow", "\x1b[93m"), ("color.bright_blue", "\x1b[94m"), ("color.bright_magenta", "\x1b[95m"),
("color.bright_cyan", "\x1b[96m"), ("color.bright_white", "\x1b[97m"),
// Bright foreground colors
("color.bright_black", "\x1b[90m"),
("color.bright_red", "\x1b[91m"),
("color.bright_green", "\x1b[92m"),
("color.bright_yellow", "\x1b[93m"),
("color.bright_blue", "\x1b[94m"),
("color.bright_magenta", "\x1b[95m"),
("color.bright_cyan", "\x1b[96m"),
("color.bright_white", "\x1b[97m"),
// Background colors
("back.black", "\x1b[40m"), ("back.red", "\x1b[41m"), ("back.green", "\x1b[42m"), ("back.yellow", "\x1b[43m"),
("back.blue", "\x1b[44m"), ("back.magenta", "\x1b[45m"), ("back.cyan", "\x1b[46m"), ("back.white", "\x1b[47m"),
// Background colors
("back.black", "\x1b[40m"),
("back.red", "\x1b[41m"),
("back.green", "\x1b[42m"),
("back.yellow", "\x1b[43m"),
("back.blue", "\x1b[44m"),
("back.magenta", "\x1b[45m"),
("back.cyan", "\x1b[46m"),
("back.white", "\x1b[47m"),
// Bright background colors
("back.bright_black", "\x1b[100m"), ("back.bright_red", "\x1b[101m"), ("back.bright_green", "\x1b[102m"),
("back.bright_yellow", "\x1b[103m"), ("back.bright_blue", "\x1b[104m"), ("back.bright_magenta", "\x1b[105m"),
("back.bright_cyan", "\x1b[106m"), ("back.bright_white", "\x1b[107m"),
];
arguments.append(&mut colors);
// Bright background colors
("back.bright_black", "\x1b[100m"),
("back.bright_red", "\x1b[101m"),
("back.bright_green", "\x1b[102m"),
("back.bright_yellow", "\x1b[103m"),
("back.bright_blue", "\x1b[104m"),
("back.bright_magenta", "\x1b[105m"),
("back.bright_cyan", "\x1b[106m"),
("back.bright_white", "\x1b[107m"),
];
arguments.append(&mut colors);
let file: File = File::open(Path::new(format!("./{}/{}.json", &self.path, &self.language).as_str())).unwrap();
let reader: BufReader<File> = BufReader::new(file);
let file: File = match File::open(Path::new(
format!("./{}/{}.json", &self.path, &self.language).as_str(),
)) {
Ok(value) => value,
Err(_error) => {
let error: errors::Error =
errors::Error::new("OS Error", "Could not open translation file", 4);
error.raise(
format!(
"File: ./{}/{}.json\nError: {}",
&self.path, &self.language, _error
)
.as_str(),
);
let json: serde_json::Value = serde_json::to_value::<T>(serde_json::from_reader::<BufReader<File>, T>(reader).unwrap()).unwrap().to_owned();
let mut result: String = match json[key].as_str() {
Some(value) => value.to_string(),
None => "".to_string()
};
return "".to_owned();
}
};
let reader: BufReader<File> = BufReader::new(file);
for (key, value) in arguments {
result = result.replace(("{{".to_owned() + key + "}}").as_str(), value);
}
let json: serde_json::Value = match serde_json::to_value::<serde_json::Value>(
match serde_json::from_reader::<BufReader<File>, serde_json::Value>(reader) {
Ok(value) => value,
Err(_error) => {
let error: errors::Error = errors::Error::new(
"Parsing error",
"Translation file could not be parsed",
5,
);
error.raise(
format!(
"File: ./{}/{}.json\nError: {}",
&self.path, &self.language, _error
)
.as_str(),
);
return result;
}
return "".to_owned();
}
},
) {
Ok(value) => value,
Err(_error) => {
let error: errors::Error =
errors::Error::new("Converting error", "Could not convert to json value", 6);
error.raise(
format!(
"File: ./{}/{}.json\nError: {}",
&self.path, &self.language, _error
)
.as_str(),
);
return "".to_owned();
}
}
.to_owned();
let mut result: String = match json[key].as_str() {
Some(value) => value.to_string(),
None => {
let error: errors::Error =
errors::Error::new("Indexing error", "Could not index json value", 6);
error.raise(
format!(
"Index: {}\nFile: ./{}/{}.json",
key, &self.path, &self.language
)
.as_str(),
);
return "".to_owned();
}
};
for (key, value) in arguments {
result = result.replace(("{{".to_owned() + key + "}}").as_str(), value);
}
return result;
}
}
/// Translates the specified key in the language specified in the config.
///
/// # Parameters
///
/// - `config`: The config object.
/// - `key`: The key to translate to.
/// - `arguments`: Optional parameter. The arguments to replace. Has to be of type `"name" = "value"`.
///
/// # Returns
///
/// A `String` containing the translated value.
///
/// # Examples
///
/// ```rust
/// # use localizer_rs;
/// # let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
/// localizer_rs::t!(config, "test");
/// localizer_rs::t!(config, "test", "variable" = "content");
/// ```
///
/// # See also
///
/// - [`Config`]
/// - [`Config::t()`]
#[macro_export]
macro_rules! t {
($config:expr, $key:expr) => {
{
$config.t($key, vec![])
}
};
($config:expr, $key:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, &str)> = vec![];
$(
arguments.push(($argument_name, $argument_value));
)*
$config.t($key, arguments)
}
};
}

72
tests/errors.rs Normal file
View File

@ -0,0 +1,72 @@
// localizer-rs error tests
// Version: 1.1.1
// Copyright (c) 2023-present ElBe Development.
// 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:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 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.
////////////////////////////////
// IMPORTS AND USE STATEMENTS //
////////////////////////////////
#[allow(unused_imports)]
use localizer_rs;
///////////
// TESTS //
///////////
#[cfg(test)]
mod tests {
#[test]
fn test_error() {
let error: localizer_rs::errors::Error =
localizer_rs::errors::Error::new("name", "description", 1);
assert_eq!(
error,
localizer_rs::errors::Error {
name: "name".to_owned(),
description: "description".to_owned(),
exit_code: 1
}
);
}
#[test]
#[ignore]
fn raise_helper() {
let error: localizer_rs::errors::Error =
localizer_rs::errors::Error::new("name", "description", 1);
error.raise("details");
}
#[test]
fn test_raise() {
let status = std::process::Command::new("cargo")
.args(&["test", "--", "--ignored"])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.expect("Unable to run program");
assert_eq!(Some(1), status.code())
}
}

View File

@ -1,5 +1,5 @@
// localizer-rs tests
// Version: 1.0.0-alpha1
// Version: 1.2.0
// Copyright (c) 2023-present ElBe Development.
@ -39,40 +39,77 @@ mod tests {
fn test_config() {
let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
assert_eq!(config, localizer_rs::Config {
path: "examples/translations".to_owned(),
language: "en".to_owned()
});
assert_eq!(
config,
localizer_rs::Config {
path: "examples/translations".to_owned(),
language: "en".to_owned()
}
);
}
#[test]
fn test_set_path() {
let mut config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
let mut config: localizer_rs::Config =
localizer_rs::Config::new("examples/translations", "en");
config.set_path("examples");
assert_eq!(config, localizer_rs::Config {
path: "examples".to_owned(),
language: "en".to_owned()
});
assert_eq!(
config,
localizer_rs::Config {
path: "examples".to_owned(),
language: "en".to_owned()
}
);
}
#[test]
fn test_set_language() {
let mut config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
let mut config: localizer_rs::Config =
localizer_rs::Config::new("examples/translations", "en");
config.set_language("not_en");
assert_eq!(config, localizer_rs::Config {
path: "examples/translations".to_owned(),
language: "not_en".to_owned()
});
assert_eq!(
config,
localizer_rs::Config {
path: "examples/translations".to_owned(),
language: "not_en".to_owned()
}
);
}
#[test]
fn test_translate() {
let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
let translation: String =
config.translate("error", vec![("details", "Something went wrong")]);
assert_eq!(
translation.as_str(),
"\x1b[31m\x1b[1mError:\x1b[0m Something went wrong"
);
}
#[test]
fn test_translate_t() {
let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
let translation: String = config.t("error", vec![("details", "Something went wrong")]);
assert_eq!(translation.as_str(), "\x1b[31m\x1b[1mError:\x1b[0m Something went wrong");
assert_eq!(
translation.as_str(),
"\x1b[31m\x1b[1mError:\x1b[0m Something went wrong"
);
}
#[test]
fn test_translate_macro() {
let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en");
let translation: String =
localizer_rs::t!(config, "error", "details" = "Something went wrong");
assert_eq!(
translation.as_str(),
"\x1b[31m\x1b[1mError:\x1b[0m Something went wrong"
);
}
}