From 4fca3645412237559be6dafedace4989817081eb Mon Sep 17 00:00:00 2001 From: ElBe <90863907+ElBe-Plaq@users.noreply.github.com> Date: Thu, 14 Sep 2023 07:33:47 +0200 Subject: [PATCH] Added everything --- .editorconfig | 18 ++ .github/.gitlint | 2 + .github/CODEOWNERS | 2 + .github/CODE_OF_CONDUCT.md | 121 +++++++++++ .github/CONTRIBUTING.md | 29 +++ .github/FUNDING.yml | 2 + .github/ISSUE_TEMPLATE/bug_report.md | 33 +++ .github/ISSUE_TEMPLATE/config.yml | 6 + .github/ISSUE_TEMPLATE/documentation.md | 7 + .github/ISSUE_TEMPLATE/enhancement.md | 7 + .github/ISSUE_TEMPLATE/feature_request.md | 19 ++ .github/PULL_REQUEST_TEMPLATE.md | 26 +++ .github/README.md | 101 +++++++++ .github/SECURITY.md | 14 ++ .github/SUPPORT.md | 7 + .github/auto_assign.yml | 5 + .github/dependabot.yml | 33 +++ .github/example.png | Bin 0 -> 65873 bytes .github/labeler.yml | 5 + .github/mergify.yml | 128 +++++++++++ .github/stale.yml | 11 + .github/workflows/dependencies.yml | 46 ++++ .github/workflows/label.yml | 25 +++ .github/workflows/megalinter.yml | 38 ++++ .github/workflows/rust.yml | 18 ++ .mega-linter.yml | 16 ++ .pre-commit-config.yaml | 38 ++++ Cargo.toml | 23 ++ LICENSE => LICENSE.txt | 0 dev-requirements.txt | 1 + examples/main.rs | 10 + examples/translations/en.json | 7 + justfile | 26 +++ src/lib.rs | 253 ++++++++++++++++++++++ tests/main.rs | 78 +++++++ 35 files changed, 1155 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/.gitlint create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/enhancement.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/README.md create mode 100644 .github/SECURITY.md create mode 100644 .github/SUPPORT.md create mode 100644 .github/auto_assign.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/example.png create mode 100644 .github/labeler.yml create mode 100644 .github/mergify.yml create mode 100644 .github/stale.yml create mode 100644 .github/workflows/dependencies.yml create mode 100644 .github/workflows/label.yml create mode 100644 .github/workflows/megalinter.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .mega-linter.yml create mode 100644 .pre-commit-config.yaml create mode 100644 Cargo.toml rename LICENSE => LICENSE.txt (100%) create mode 100644 dev-requirements.txt create mode 100644 examples/main.rs create mode 100644 examples/translations/en.json create mode 100644 justfile create mode 100644 src/lib.rs create mode 100644 tests/main.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c7e631c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# Editorconfig to keep settings the same across devices and editors +# https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +indent_style = tab +insert_final_newline = true +tab_width = 4 +trim_trailing_whitespace = true + +[*.{html,md,py,rs}] +indent_size = 4 +indent_style = space + +[*.{yml,yaml}] +indent_style = space diff --git a/.github/.gitlint b/.github/.gitlint new file mode 100644 index 0000000..9755ab3 --- /dev/null +++ b/.github/.gitlint @@ -0,0 +1,2 @@ +[general] +ignore=B6 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..88d7df4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Global owner +* @ElBe-Plaq diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..defbeba --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,121 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement on +[our discord server](https://discord.gg/JVyyDukQqV). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html), +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html). + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at +[https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6bfdfdc --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# How to contribute to this project + +Did you find a bug? You want to have a feature implemented? You made code better? +Please follow the instructions below on how to contribute to this project. + +## Notes + +Please do **not** report an issue of pull request if the issue is a security vulnerability. +There is a different way to report such issues. Issues or pull requests about a vulnerability will be removed. + +
+

Report vulnerabilities here

+
+ +## Contributing + +For any issues or proposals, open an issue. If you already wrote your proposed change(s), please open an issue +and a pull request. See below for making changes on your own. + +### 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. +5. Create a pull request from your fork. +6. If it gets accepted, your changes will be in the next release. + +To everyone who has reported issues, proposed changes or just helped with peoples questions: Thank you! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fa8cce5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +--- +ko_fi: elbeplaq diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..161380c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] TITLE" +labels: Bug +assignees: ElBe-Plaq +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Rust version [eg. 1.69] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a61c085 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +--- +blank_issues_enabled: false +contact_links: + - name: Discord Server + url: https://discord.gg/JVyyDukQqV + about: Please ask and answer questions here or just chat with the communtiy. diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..d45d81d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,7 @@ +--- +name: Documentation +about: Improvements to the documentation. +title: "[DOCUMENTATION] TITLE" +labels: Documentation +assignees: ElBe-Plaq +--- diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md new file mode 100644 index 0000000..54731f2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -0,0 +1,7 @@ +--- +name: Enhancement +about: Improves something already existing. +title: "[ENHANCEMENT] TITLE" +labels: Enhancement +assignees: ElBe-Plaq +--- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..36492b6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE] TITLE" +labels: Feature request +assignees: ElBe-Plaq +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6f9b702 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +# Description + +Summary of all the changes made. + +Closes #(issues that get closed, if this pull request gets closed) + +## Type of change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update +- [ ] Something else + +# Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new errors or linter warnings +- [ ] My changes generate no problems with other code +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing tests passed locally with my changes diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..75df90e --- /dev/null +++ b/.github/README.md @@ -0,0 +1,101 @@ +

+ localizer-rs +

+

+ Localizer helps localize (translate) your rust applications using json files. +

+

+ + + + +

+ + + +## About this project + +Localizer is a tool to translate text using json files. + +## Installing + +Run the following command to add the package to your dependencies: + +```bash + +$ cargo add localizer-rs +... + +``` + +### Git + +To clone the repository locally using git run `git clone https://github.com/ElBe-Development/localizer-rs.git`. + +## Usage + +To use localizer-rs, you need a directory (eg. `translations`) with your translations files (eg. `en.json`). You then need to follow these steps: + +1. Import the localizer-rs crate: + + ```rust + + use localizer_rs; + + ``` + +2. Create a new config object: + + ```rust + + let config = localizer_rs::Config::new("DIRECTORY NAME", "LANGUAGE NAME"); + + ``` + +3. Translate your text: + + ```rust + + config.t("key", vec!["placeholder", "value"]); + + ``` + +## Example + +With the following `en.json` file. + +```json + +{ + "error": "{{color.red}}{{bold}}Error:{{end}} Something went wrong: {{details}}." +} + +``` + +And the following rust code. + +```rust + +use localizer_rs; + +fn main() { + let config: localizer_rs::Config = localizer_rs::Config::new("translations", "en"); + + println!("{:}", config.t("error", vec![("details", "Path not found")])); +} + +``` + +You will get the following output: + +```bash + +Error: Something went wrong: Path not found. + +``` + +Where `Error:` is red and bold. + +## Contact + +To contact us, get help or just chat with others, you can visit [our discord server](https://discord.gg/JVyyDukQqV). diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..a6b3b74 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| -------- | ------------------ | +| `v1.0.0` | :white_check_mark: | + +## Reporting a Vulnerability + +To report a vulnerability, go to [our discord server](https://discord.gg/JVyyDukQqV), write an +[E-Mail](mailto:elbe.dev.plaq@gmail.com) or report it via +[GitHubs security reporting system](https://github.com/ElBe-Development/localizer-rs/security/advisories/new) +(preferred). diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..bd2a2fd --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,7 @@ +# Support + +You can get support on: + +- [our discord server](https://discord.gg/JVyyDukQqV) +- [GitHub discussions](https://github.com/ElBe-Development/localizer-rs/discussions) +- [GitHub issues](https://github.com/ElBe-Development/localizer-rs/issues) diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml new file mode 100644 index 0000000..fb03d3c --- /dev/null +++ b/.github/auto_assign.yml @@ -0,0 +1,5 @@ +--- +addReviewers: false +addAssignees: true +assignees: + - ElBe-Plaq diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ea3679d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,33 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +--- +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + assignees: + - "ElBe-Plaq" + labels: + - "dependencies" + + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "daily" + assignees: + - "ElBe-Plaq" + labels: + - "dependencies" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + assignees: + - "ElBe-Plaq" + labels: + - "dependencies" diff --git a/.github/example.png b/.github/example.png new file mode 100644 index 0000000000000000000000000000000000000000..80da4605213dc1f5ce7aab20a20d5ad2319b98b6 GIT binary patch literal 65873 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

    `g0tHL@_}Ccd+uo3 zRl^uI=kbS0gFA8|176PbU-%3VL5ZMH56d%2EQq+fs$^`i5NP+0AD4&q<3t3xb%P2D zr$Dj+li7T?&5gqmyqNEycynKKhg4wV?s+}?D{3}8$IdISnvykN8lLM>6Bg3qRM?wF z-PEC5-3RQC^dz=I?zZRG~d*$|pu z?#wTbR<~f$3x{iPJ0eNdeEz*UKAImb*i1?`_Evmoo*w=}B}0_7`@PN87jDcHC3&}a z7y$@Z1AODgO$PPH3I;&A!lgc1+{v31mVE^y_#4#NJxvaI+pRR?b!C7GIXn&vt-kNx z+b@12FzgBc>)(FopAw_OkZ;LKWcU?FAX>FO!D*VH+E=-B(3M-Mkr?@et%hMBqj#KS zDYI_2{4}E{FYtR%Es)%R2oGVXxJ^+xH#6u|H@nwgK`}E_Va}o`e@j5CYRWjt5$)`d zU&`lxWjp3IJwEV>{TR_t!G2L!e)_mV(JJJB08rV`=>Px# literal 0 HcmV?d00001 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..ac92c4e --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,5 @@ +--- +example: + - "examples/*" +dependencies: + - "dev-requirements.txt" diff --git a/.github/mergify.yml b/.github/mergify.yml new file mode 100644 index 0000000..cb54a5e --- /dev/null +++ b/.github/mergify.yml @@ -0,0 +1,128 @@ +--- +pull_request_rules: + - name: Merge trusted bot updates, no conflicts are present and approved + conditions: + - or: + - "label~=Approved" + - "-#approved-reviews-by=0" + - "-conflict" + - "-draft" + - "-locked" + - "#check-failure=0" + - "author~=.*bot.*" + actions: + comment: + message: | + # Merging pull request + + Checks: + | Name | Status | + |------|--------| + {% for check in check_success %}| {{check}} | :white_check_mark: | + {% endfor %} + merge: + commit_message_template: | + {{title}} + + {{title}} in #{{number}} by @{{author}}, contributing to {{milestone}} + + Changed files: + {% for file in files %}- '{{file}}' + {% endfor %} + + Approved by: @{{ approved_reviews_by | join(', @') }} + + + {% for commit in commits %}Co-authored-by: {{commit.author}} <{{commit.email_author}}> + {% endfor %} + method: merge + delete_head_branch: + - name: Merge if approved, no conflicts are present and it's not a WIP + conditions: + - or: + - "-#approved-reviews-by=0" + - "label~=Approved" + - "label~=Able to merge" + - "-conflict" + - "-draft" + - "-locked" + - "#check-failure=0" + actions: + comment: + message: | + # Merging pull request + + Checks: + | Name | Status | + |------|--------| + {% for check in check_success %}| {{check}} | :white_check_mark: | + {% endfor %} + merge: + commit_message_template: | + {{title}} + + {{title}} in #{{number}} by @{{author}}, contributing to {{milestone}} + + Changed files: + {% for file in files %}- '{{file}}' + {% endfor %} + + Approved by: @{{ approved_reviews_by | join(', @') }} + + + {% for commit in commits %}Co-authored-by: {{commit.author}} <{{commit.email_author}}> + {% endfor %} + method: merge + delete_head_branch: + - name: Add review requested label and request review from ElBe + conditions: + - or: + - "#approved-reviews-by=0" + - "-label~=Approved" + - "-title~=^[WIP].*" + - "-label~=Declined" + - "-label~=Review requested" + - "-draft" + - "-locked" + - "-conflict" + actions: + label: + add: + - Review requested + request_reviews: + users: + - ElBe-Plaq + - name: Warn on conflicts and add label + conditions: + - conflict + actions: + comment: + message: "@{{author}} this pull request has one or more conflicts." + label: + add: + - Invalid + remove: + - Review requested + - name: Remove invalid label if not needed + conditions: + - -conflict + actions: + label: + add: + - Review requested + remove: + - Invalid + - name: Warn on failed checks + conditions: + - "-#check-failure=0" + actions: + comment: + message: | + # Checks failed + + Checks: + | Name | Status | + |------|--------| + {% for check in check_success %}| {{check}} | :white_check_mark: | + {% endfor %}{% for check in check_failure %}| {{check}} | :x: | + {% endfor %} diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..a4d7996 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,11 @@ +--- +daysUntilStale: 60 +daysUntilClose: 7 +exemptLabels: + - Security +staleLabel: stale +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +closeComment: false diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 0000000..2962d4c --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,46 @@ +--- +name: Dependencies +permissions: read-all + +on: + push: + paths: ["dev-requirements.txt", "Cargo.toml"] + + pull_request: + paths: ["dev-requirements.txt", "Cargo.toml"] + +jobs: + install-python-dependencies: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies from txt files + run: | + python -m pip install --upgrade pip + pip install -r dev-requirements.txt + install-rust-dependencies: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install dependencies from Cargo.toml + run: cargo update + - name: Install dev-dependencies from Cargo.toml + run: cargo test --no-run + # ^ This is the only way to install dev-dependencies + + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + if: ${{ github.event_name != 'push' }} + - name: Dependency Review + if: ${{ github.event_name != 'push' }} + uses: actions/dependency-review-action@v3 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 0000000..34a042d --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,25 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler + +--- +name: Labeler +permissions: + pull-requests: write + +on: [pull_request_target] + +jobs: + label: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/megalinter.yml b/.github/workflows/megalinter.yml new file mode 100644 index 0000000..7c38085 --- /dev/null +++ b/.github/workflows/megalinter.yml @@ -0,0 +1,38 @@ +--- +name: MegaLinter +permissions: read-all + +on: + push: + pull_request: + branches: [main] + +env: + APPLY_FIXES: VALIDATE_ALL_CODEBASE + APPLY_FIXES_EVENT: all + APPLY_FIXES_MODE: commit + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + build: + name: MegaLinter + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} + fetch-depth: 0 + - name: MegaLinter + id: ml + uses: oxsecurity/megalinter@v7 + env: + VALIDATE_ALL_CODEBASE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..2b42c87 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,18 @@ +name: Rust +permissions: read-all + +on: + [push, pull_request] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.mega-linter.yml b/.mega-linter.yml new file mode 100644 index 0000000..b10c5b8 --- /dev/null +++ b/.mega-linter.yml @@ -0,0 +1,16 @@ +--- +DISABLE_ERRORS_LINTERS: + - SPELL_CSPELL + - REPOSITORY_DEVSKIM # cspell:disable-line + - MAKEFILE_CHECKMAKE # cspell:disable-line + - HTML_HTMLHINT # cspell:disable-line + - MARKDOWN_MARKDOWN_LINK_CHECK + - REPOSITORY_DUSTILOCK # cspell:disable-line + - JAVASCRIPT_STANDARD + - REPOSITORY_TRUFFLEHOG # cspell:disable-line + - SPELL_LYCHEE +FILTER_REGEX_EXCLUDE: .*mypy.* # cspell:disable-line +EDITORCONFIG_EDITORCONFIG_CHECKER_FILTER_REGEX_EXCLUDE: "Docs/acknowledgements.md|src/Installer/Linux/installer.bash" +REPORT_OUTPUT_FOLDER: none +SHOW_ELAPSED_TIME: true +VALIDATE_ALL_CODEBASE: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6c07b01 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + - id: check-executables-have-shebangs + - id: check-json + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-yaml + - id: pretty-format-json + args: [--autofix] + - id: requirements-txt-fixer + - id: trailing-whitespace + - repo: https://github.com/jorisroovers/gitlint + rev: v0.19.1 + hooks: + - id: gitlint + args: [--config=.github\.gitlint, --msg-filename] + - repo: https://github.com/doublify/pre-commit-rust + rev: v1.0 + hooks: + - id: fmt + - id: cargo-check + - repo: local + hooks: + - id: clippy + name: Run clippy linter + entry: cargo clippy -- -A non_snake_case + language: system + pass_filenames: false +ci: + skip: + - fmt + - cargo-check + - clippy diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e7c9c3a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "localizer-rs" +description = "Localizer helps localize (translate) your rust applications using json files." +version = "1.0.0" +authors = [ + "ElBe-Plaq " +] +edition = "2021" +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" +keywords = ["i18n", "L10n", "json", "local", "translation"] +categories = [ + "internationalization", + "localization" +] +publish = ["localizer-rs"] + +[dependencies] +serde = "1.0.188" +serde_json = "1.0.106" diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..60d2bd3 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1 @@ +pre-commit==3.3.2; python_version>='3.8' diff --git a/examples/main.rs b/examples/main.rs new file mode 100644 index 0000000..1086aa7 --- /dev/null +++ b/examples/main.rs @@ -0,0 +1,10 @@ +use localizer_rs; + +fn main() { + 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!("{:}", config.t("all", vec![])); +} \ No newline at end of file diff --git a/examples/translations/en.json b/examples/translations/en.json new file mode 100644 index 0000000..adebba9 --- /dev/null +++ b/examples/translations/en.json @@ -0,0 +1,7 @@ +{ + "test": "Something that can be translated.", + "error": "{{color.red}}{{bold}}Error:{{end}} {{details}}", + "success": "{{color.green}}{{bold}}Success:{{end}} Successfully transferred {{balance}} to {{user}}", + + "all": "{{bold}}{{underline}}Formatting options:{{end}}\n\n{{bold}}Bold text{{end}}\n{{italic}}Italic text{{end}}\n{{underline}}Underlined text{{end}}\n{{overline}}Overlined text{{end}}\n\n{{bold}}{{underline}}Colored text:{{end}}\n\n{{back.white}}{{color.black}}Black text{{end}}\n{{color.red}}Red text{{end}}\n{{color.green}}Green text{{end}}\n{{color.yellow}}Yellow text{{end}}\n{{color.blue}}Blue text{{end}}\n{{color.magenta}}Magenta text{{end}}\n{{color.cyan}}Cyan text{{end}}\n{{color.white}}White text{{end}}\n\n{{bold}}{{underline}}Bright colored text:{{end}}\n\n{{color.bright_black}}Bright black text{{end}}\n{{color.bright_red}}Bright red text{{end}}\n{{color.bright_green}}Bright green text{{end}}\n{{color.bright_yellow}}Bright yellow text{{end}}\n{{color.bright_blue}}Bright blue text{{end}}\n{{color.bright_magenta}}Bright magenta text{{end}}\n{{color.bright_cyan}}Bright cyan text{{end}}\n{{color.bright_white}}Bright white text{{end}}\n\n{{bold}}{{underline}}Colored background:{{end}}\n\n{{back.black}}Black background{{end}}\n{{back.red}}Red background{{end}}\n{{back.green}}Green text (background:{{end}}\n{{back.yellow}}Yellow background{{end}}\n{{back.blue}}Blue background{{end}}\n{{back.magenta}}Magenta background{{end}}\n{{back.cyan}}Cyan background{{end}}\n{{color.black}}{{back.white}}White background{{end}}\n{{back.bright_black}}Bright black background{{end}}\n{{back.bright_red}}Bright red background{{end}}\n{{back.bright_green}}Bright green background{{end}}\n{{back.bright_yellow}}Bright yellow background{{end}}\n{{back.bright_blue}}Bright blue background{{end}}\n{{back.bright_magenta}}Bright magenta background{{end}}\n{{back.bright_cyan}}Bright cyan background{{end}}\n{{back.bright_white}}Bright white background{{end}}" +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..a3b4dd3 --- /dev/null +++ b/justfile @@ -0,0 +1,26 @@ +alias b := build +alias c := clean +alias l := lint +alias r := run +alias t := test + +# Compiles the rust source files +build *ARGUMENTS: + cargo build --release *ARGUMENTS + +# Removes temporary files +clean: + rm -rf target + rm -rf Tools/__pycache__ + +# Lints the rust source files +lint *ARGUMENTS: + cargo check *ARGUMENTS + +# Compiles and executes the main.rs file +run *ARGUMENTS: + cargo run *ARGUMENTS + +# Runs the unittests +test *ARGUMENTS: + cargo test *ARGUMENTS diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4d84996 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,253 @@ +// localizer-rs +// Version: 1.0.0 + +// 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::fs::File; +use std::io::BufReader; +use std::path::Path; +use std::process::exit; + +use serde_json; + + +/////////////////// +// CONFIG OBJECT // +/////////////////// + +/// Localization config object. +/// +/// Use [`Config::new()`] to create config objects instead of using this struct. +/// +/// # Parameters +/// +/// - `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 +/// +/// A new `Config` object with the specified path and language. +/// +/// # Examples +/// +/// ```rust +/// # use localizer_rs; +/// localizer_rs::Config { +/// path: "path".to_owned(), +/// language: "language".to_owned() +/// }; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Config { + pub path: String, + pub language: String +} + + +////////////////////// +// CONFIG FUNCTIONS // +////////////////////// + +impl Config { + /// Creates a new config object. + /// + /// # Parameters + /// + /// - `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 + /// + /// A new `Config` object with the specified path and language. + /// + /// # Panics + /// + /// Panics if the Path provided is invalid. + /// + /// # Examples + /// + /// ```rust + /// # use localizer_rs; + /// localizer_rs::Config::new("examples/translations", "language"); + /// ``` + /// + /// # 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(); + + 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. + /// + /// # Returns + /// + /// The modified `Config` object with the specified path. + /// + /// # Panics + /// + /// Panics if the Path provided is invalid. + /// + /// # Examples + /// + /// ```rust + /// # use localizer_rs; + /// # let 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); + + 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); + } + } + + self.path = String::from(path.to_owned().to_str().expect("Expected valid path")); + return self; + } + + /// Sets the language for the config object. + /// + /// # Parameters + /// + /// - `self`: The config object. + /// - `language`: The language to translate to. + /// + /// # Returns + /// + /// The modified `Config` object with the specified language. + /// + /// # Examples + /// + /// ```rust + /// # use localizer_rs; + /// # let 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; + } + + /// 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. + /// + /// # Examples + /// + /// ```rust + /// # use localizer_rs; + /// # let config: localizer_rs::Config = localizer_rs::Config::new("examples/translations", "en"); + /// config.t("test", vec![]); + /// ``` + /// + /// # See also + /// + /// - [`Config`] + pub fn t(&self, key: &str, arguments: Vec<(&str, &str)>) -> String { + return self.translate::(key, arguments); + } + + fn translate(&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"), + + // 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"), + + // 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); + + let file: File = File::open(Path::new(format!("./{}/{}.json", &self.path, &self.language).as_str())).unwrap(); + let reader: BufReader = BufReader::new(file); + + let json: serde_json::Value = serde_json::to_value::(serde_json::from_reader::, T>(reader).unwrap()).unwrap().to_owned(); + let mut result: String = match json[key].as_str() { + Some(value) => value.to_string(), + None => "".to_string() + }; + + for (key, value) in arguments { + result = result.replace(("{{".to_owned() + key + "}}").as_str(), value); + } + + return result; + } +} diff --git a/tests/main.rs b/tests/main.rs new file mode 100644 index 0000000..4963182 --- /dev/null +++ b/tests/main.rs @@ -0,0 +1,78 @@ +// localizer-rs tests +// Version: 1.0.0-alpha1 + +// 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_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() + }); + } + + #[test] + fn test_set_path() { + 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() + }); + + } + + #[test] + fn test_set_language() { + 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() + }); + } + + #[test] + fn test_translate() { + 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"); + } +}