Compare commits

..

6 commits

Author SHA1 Message Date
4db6ef807b Fix isort 2023-11-23 22:31:16 +01:00
df7eb6b253 Change the way the showcase JS works.
- Put the images in a language folder ("en", "fr"), which will make it
  easier to add orther languages later on.
- Resize the images to fit the already existing ones.
- Add a `display_showcase` parameter to the
  `list_bills` and `home` views.
2023-11-23 19:36:39 +01:00
Theo
ff4b6534d5 resolving added files issue 2023-11-21 10:09:25 +01:00
Theo
6741d5f14d adding the english version of the comics, including the modifications in the frontend structure 2023-11-20 15:21:49 +01:00
Theo
44323f8cf9 revert to basic html version 2023-11-14 11:37:08 +01:00
Theo
15c1d1b8d6 html update 2023-11-14 11:02:20 +01:00
116 changed files with 1658 additions and 5980 deletions

View file

@ -14,7 +14,9 @@ CONTRIBUTORS
docker-compose.* docker-compose.*
Dockerfile Dockerfile
docs docs
LICENSE
Makefile Makefile
MANIFEST.in MANIFEST.in
README.md
SECURITY.md SECURITY.md
tox.ini tox.ini

View file

@ -1,122 +0,0 @@
name: CI
on:
push:
branches: [ 'main', 'stable-*' ]
pull_request:
branches: [ 'main', 'stable-*' ]
jobs:
lint:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v4
with:
python-version: "3.11"
- name: Run Lint
run: make lint
test:
# Dependency on linting to avoid running our expensive matrix test for nothing
needs: lint
runs-on: ubuntu-22.04
# Use postgresql and MariaDB versions of Debian bookworm
services:
postgres:
image: postgres:15
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ihatemoney
POSTGRES_DB: ihatemoney_ci
options:
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
mariadb:
image: mariadb:10.11
env:
MARIADB_ROOT_PASSWORD: ihatemoney
MARIADB_DATABASE: ihatemoney_ci
options: >-
--health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
ports:
- 3306:3306
strategy:
fail-fast: false
matrix:
python-version: [3.9, "3.10", "3.11", "3.12"]
dependencies: [normal]
database: [sqlite]
# Test other databases with only a few versions of Python
# (Debian bullseye has 3.9, bookworm has 3.11)
include:
- python-version: 3.9
dependencies: normal
database: postgresql
- python-version: 3.9
dependencies: normal
database: mariadb
- python-version: 3.11
dependencies: normal
database: postgresql
- python-version: 3.11
dependencies: normal
database: mariadb
# Try a few variants with the minimal versions supported
- python-version: 3.9
dependencies: minimal
database: sqlite
- python-version: "3.10"
dependencies: minimal
database: sqlite
- python-version: "3.11"
dependencies: minimal
database: sqlite
- python-version: "3.11"
dependencies: minimal
database: postgresql
- python-version: "3.11"
dependencies: minimal
database: mariadb
- python-version: "3.12"
dependencies: minimal
database: sqlite
steps:
- uses: actions/checkout@v3
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v4
with:
python-version: ${{ matrix.python-version }}
- name: Change dependencies to minimal supported versions
# This sed comment installs the minimal supported version
# for all versions except for "requires-python"
# This is to detect that the minimum versions are really
# supported, in the CI
run: sed -i -e '/requires-python/!s/>=/==/g; /requires-python/!s/~=.*==\(.*\)/==\1/g; /requires-python/!s/~=/==/g;' pyproject.toml
if: matrix.dependencies == 'minimal'
- name: Run tests
run: uv run --extra dev --extra database pytest .
env:
# Setup the DATABASE_URI depending on the matrix we are using.
TESTING_SQLALCHEMY_DATABASE_URI: ${{
matrix.database == 'sqlite'
&& 'sqlite:///budget.db'
|| matrix.database == 'postgresql'
&& 'postgresql+psycopg2://postgres:ihatemoney@localhost:5432/ihatemoney_ci'
|| 'mysql+pymysql://root:ihatemoney@localhost:3306/ihatemoney_ci'
}}
docs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v4
with:
python-version: "3.11"
- name: Build docs
run: make build-docs

View file

@ -1,16 +1,16 @@
name: Docker build name: CI to Docker Hub
on: on:
push: push:
tags: ['*'] tags: ['*']
branches: [ 'main', 'stable-*' ] branches: [ master ]
pull_request: pull_request:
branches: [ 'main', 'stable-*' ] branches: [ master ]
jobs: jobs:
test: test:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -18,8 +18,8 @@ jobs:
- name: Test image # Checks we are able to run the container. - name: Test image # Checks we are able to run the container.
run: docker compose -f docker-compose.test.yml run sut run: docker compose -f docker-compose.test.yml run sut
build_upload: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
needs: test needs: test
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
steps: steps:

103
.github/workflows/test-docs.yml vendored Normal file
View file

@ -0,0 +1,103 @@
name: Test & Docs
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
# Use postgresql and MariaDB versions of Debian buster
services:
postgres:
image: postgres:11
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ihatemoney
POSTGRES_DB: ihatemoney_ci
options:
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
mariadb:
image: mariadb:10.3
env:
MARIADB_ROOT_PASSWORD: ihatemoney
MARIADB_DATABASE: ihatemoney_ci
options: >-
--health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
ports:
- 3306:3306
strategy:
fail-fast: false
matrix:
python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
dependencies: [normal]
database: [sqlite]
# Test other databases only with one version of Python (Debian buster has 3.7)
include:
- python-version: 3.7
dependencies: normal
database: postgresql
- python-version: 3.7
dependencies: normal
database: mariadb
# Try a few variants with the minimal versions supported
- python-version: 3.7
dependencies: minimal
database: sqlite
- python-version: 3.7
dependencies: minimal
database: postgresql
- python-version: 3.7
dependencies: minimal
database: mariadb
- python-version: 3.9
dependencies: minimal
database: sqlite
- python-version: "3.10"
dependencies: minimal
database: sqlite
- python-version: "3.11"
dependencies: minimal
database: sqlite
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: '**/pyproject.toml'
- name: Change dependencies to minimal supported versions
run: sed -i -e 's/>=/==/g; s/~=.*==\(.*\)/==\1/g; s/~=/==/g;' pyproject.toml
if: matrix.dependencies == 'minimal'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
# Run tox using the version of Python in `PATH`
- name: Run Tox with sqlite
run: tox -e py
if: matrix.database == 'sqlite'
env:
TESTING_SQLALCHEMY_DATABASE_URI: 'sqlite:///budget.db'
- name: Run Tox with postgresql
run: tox -e py
if: matrix.database == 'postgresql'
env:
TESTING_SQLALCHEMY_DATABASE_URI: 'postgresql+psycopg2://postgres:ihatemoney@localhost:5432/ihatemoney_ci'
- name: Run Tox with mariadb
run: tox -e py
if: matrix.database == 'mariadb'
env:
TESTING_SQLALCHEMY_DATABASE_URI: 'mysql+pymysql://root:ihatemoney@localhost:3306/ihatemoney_ci'
- name: Run Lint & Docs
run: tox -e lint_docs
if: matrix.python-version == '3.11'

1
.gitignore vendored
View file

@ -1,6 +1,5 @@
*.pyc *.pyc
*.egg-info *.egg-info
*.mo
dist dist
.venv .venv
docs/_build/ docs/_build/

14
.isort.cfg Normal file
View file

@ -0,0 +1,14 @@
[settings]
# Needed for black compatibility
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
line_length=88
combine_as_imports=True
profile = "black"
# If set, imports will be sorted within their section independent to the import_type.
force_sort_within_sections=True
# skip
skip_glob=.local,**/migrations/**,**/node_modules/**,**/node-forge/**

View file

@ -2,40 +2,10 @@
This document describes changes between each past release. This document describes changes between each past release.
## 6.2.0 (unreleased) ## 6.1.2 (unreleased)
- Add support for python 3.12 (#757)
- Migrate from setup.cfg to pyproject.toml (#1243)
- Update to wtforms 3.1 (#1248)
- Document [repository rules](https://ihatemoney.readthedocs.io/en/latest/contributing.html#repository-rules) (#1253)
- Add "reimbursement" bills and allow to create them directly from the "Settle" page (#1290)
- Remove support for python 3.7
- Replace the black linter by ruff
- Replace virtualenv and pip by uv
- Remove tox
## 6.1.5 (2024-03-19)
- Fix README and changelog not being displayed on PyPI
- Fix ability to change project settings when project has existing currency (#1292)
- Update translations for Dutch and German
## 6.1.4 (2023-12-14) - Nothing changed yet.
- Fix missing markdown include in manifest (#1274)
- Update translations for Chinese, Turkish, Czech, Spanish (Latin America), Swedish
## 6.1.3 (2023-11-23)
- Revert update to flask and werkzeug 2.3 because of a regression (see #1272)
## 6.1.2 (2023-11-19)
- Fix password generation command line crash (#1242)
- Update to flask and werkzeug 2.3 (#1244)
## 6.1.1 (2023-10-04) ## 6.1.1 (2023-10-04)

3
MANIFEST.in Normal file
View file

@ -0,0 +1,3 @@
include *.rst
recursive-include ihatemoney *.rst *.py *.yaml *.po *.mo *.html *.css *.js *.eot *.svg *.woff *.txt *.png *.webp *.ini *.cfg *.j2 *.jpg *.gif *.ico *.xml
include LICENSE CONTRIBUTORS CHANGELOG.rst

View file

@ -1,40 +1,60 @@
VIRTUALENV = uv venv VIRTUALENV = python3 -m venv
SPHINX_BUILDDIR = docs/_build
VENV := $(shell realpath $${VIRTUAL_ENV-.venv}) VENV := $(shell realpath $${VIRTUAL_ENV-.venv})
BIN := uv tool run PYTHON = $(VENV)/bin/python3
PIP := uv pip
PYTHON = $(BIN)/python3
DEV_STAMP = $(VENV)/.dev_env_installed.stamp DEV_STAMP = $(VENV)/.dev_env_installed.stamp
INSTALL_STAMP = $(VENV)/.install.stamp INSTALL_STAMP = $(VENV)/.install.stamp
TEMPDIR := $(shell mktemp -d) TEMPDIR := $(shell mktemp -d)
ZOPFLIPNG := zopflipng ZOPFLIPNG := zopflipng
MAGICK_MOGRIFY := mogrify MAGICK_MOGRIFY := mogrify
.PHONY: all
all: install ## Alias for install
.PHONY: install
install: virtualenv pyproject.toml $(INSTALL_STAMP) ## Install dependencies
$(INSTALL_STAMP):
$(VENV)/bin/pip install -U pip
$(VENV)/bin/pip install -e .
touch $(INSTALL_STAMP)
.PHONY: virtualenv .PHONY: virtualenv
virtualenv: $(PYTHON) virtualenv: $(PYTHON)
$(PYTHON): $(PYTHON):
$(VIRTUALENV) $(VENV) $(VIRTUALENV) $(VENV)
.PHONY: install-dev
install-dev: virtualenv pyproject.toml $(INSTALL_STAMP) $(DEV_STAMP) ## Install development dependencies
$(DEV_STAMP): $(PYTHON)
$(VENV)/bin/pip install -Ue .[dev]
touch $(DEV_STAMP)
.PHONY: remove-install-stamp
remove-install-stamp:
rm $(INSTALL_STAMP)
.PHONY: update
update: remove-install-stamp install ## Update the dependencies
.PHONY: serve .PHONY: serve
serve: build-translations ## Run the ihatemoney server serve: install ## Run the ihatemoney server
@echo 'Running ihatemoney on http://localhost:5000' @echo 'Running ihatemoney on http://localhost:5000'
FLASK_DEBUG=1 FLASK_APP=ihatemoney.wsgi uv run flask run --host=0.0.0.0 FLASK_DEBUG=1 FLASK_APP=ihatemoney.wsgi $(VENV)/bin/flask run --host=0.0.0.0
.PHONY: test .PHONY: test
test: test: install-dev ## Run the tests
uv run --extra dev --extra database pytest . $(VENV)/bin/tox
.PHONY: lint .PHONY: black
lint: black: install-dev ## Run the tests
uv tool run ruff check . $(VENV)/bin/black --target-version=py37 .
uv tool run vermin --no-tips --violations -t=3.8- .
.PHONY: format .PHONY: isort
format: isort: install-dev ## Run the tests
uv tool run ruff format . $(VENV)/bin/isort .
.PHONY: release .PHONY: release
release: # Release a new version (see https://ihatemoney.readthedocs.io/en/latest/contributing.html#how-to-release) release: install-dev ## Release a new version (see https://ihatemoney.readthedocs.io/en/latest/contributing.html#how-to-release)
uv run --extra dev fullreleas $(VENV)/bin/fullrelease
.PHONY: compress-showcase .PHONY: compress-showcase
compress-showcase: compress-showcase:
@ -52,30 +72,27 @@ compress-assets: compress-showcase ## Compress static assets
.PHONY: build-translations .PHONY: build-translations
build-translations: ## Build the translations build-translations: ## Build the translations
uv run --extra dev pybabel compile -d ihatemoney/translations $(VENV)/bin/pybabel compile -d ihatemoney/translations
.PHONY: extract-translations .PHONY: update-translations
extract-translations: ## Extract new translations from source code update-translations: ## Extract new translations from source code
uv run --extra dev pybabel extract --add-comments "I18N:" --strip-comments --omit-header --no-location --mapping-file ihatemoney/babel.cfg -o ihatemoney/messages.pot ihatemoney $(VENV)/bin/pybabel extract --add-comments "I18N:" --strip-comments --omit-header --no-location --mapping-file ihatemoney/babel.cfg -o ihatemoney/messages.pot ihatemoney
uv run --extra dev pybabel update -i ihatemoney/messages.pot -d ihatemoney/translations/ $(VENV)/bin/pybabel update -i ihatemoney/messages.pot -d ihatemoney/translations/
.PHONY: create-database-revision .PHONY: create-database-revision
create-database-revision: ## Create a new database revision create-database-revision: ## Create a new database revision
@read -p "Please enter a message describing this revision: " rev_message; \ @read -p "Please enter a message describing this revision: " rev_message; \
uv run python -m ihatemoney.manage db migrate -d ihatemoney/migrations -m "$${rev_message}" $(PYTHON) -m ihatemoney.manage db migrate -d ihatemoney/migrations -m "$${rev_message}"
.PHONY: create-empty-database-revision .PHONY: create-empty-database-revision
create-empty-database-revision: ## Create an empty database revision create-empty-database-revision: ## Create an empty database revision
@read -p "Please enter a message describing this revision: " rev_message; \ @read -p "Please enter a message describing this revision: " rev_message; \
uv run python -m ihatemoney.manage db revision -d ihatemoney/migrations -m "$${rev_message}" $(PYTHON) -m ihatemoney.manage db revision -d ihatemoney/migrations -m "$${rev_message}"
.PHONY: clean .PHONY: clean
clean: ## Destroy the virtual environment clean: ## Destroy the virtual environment
rm -rf .venv rm -rf .venv
build-docs:
uv run --extra doc sphinx-build -a -n -b html -d docs/_build/doctrees docs docs/_build/html
.PHONY: help .PHONY: help
help: ## Show the help indications help: ## Show the help indications
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View file

@ -22,41 +22,10 @@ highly encouraged to do so.
## Requirements ## Requirements
- **Python**: version 3.8 to 3.12. - **Python**: version 3.7 to 3.11.
- **Backends**: SQLite, PostgreSQL, MariaDB (version 10.3.2 or above), - **Backends**: SQLite, PostgreSQL, MariaDB (version 10.3.2 or above),
Memory. Memory.
## Current direction (as of 2024)
Ihatemoney was started in 2011, and we believe the project has reached a certain
level of maturity now. The overall energy of contributors is not as high as it
used to be.
In addition, there are now several self-hosted alternatives (for instance
[cospend](https://github.com/julien-nc/cospend-nc/tree/main),
[spliit](https://github.com/spliit-app/spliit)).
As maintainers, we believe that the project is still relevant but should gear
towards some kind of "maintenance mode":
* **Simplicity** is now the main goal of the project. It has always been a compass
for the project, and the resulting software is appreciated by both users and
server administrators. For us, "simplicity" is positive and encompasses both
technical aspects (very few javascript code, manageable dependencies, small code
size...) and user-visible aspects (straightforward interface, no need to create
accounts for people you invite, same web interface on mobile...)
* **Stability** is prioritized over adding major new features. We found ourselves
complexifying the codebase (and the interface) while accepting some
contributions. Our goal now is to have a minimal set of features that do most of
the job. We believe this will help lower the maintainance burden.
* **User interface and user experience improvements** are always super welcome !
It is still possible to propose new features, but they should fit into
this new direction. Simplicity of the UI/UX and simplicity of the technical
implementation will be the main factors when deciding to accept new features.
## Contributing ## Contributing
Do you wish to contribute to IHateMoney? Fantastic! There's a lot of Do you wish to contribute to IHateMoney? Fantastic! There's a lot of

View file

@ -4,9 +4,9 @@
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ------------------ |
| 6.2.x | :heavy_check_mark: | | 5.0.x | :heavy_check_mark: |
| 6.1.x | :heavy_check_mark: | | 4.1.x | :heavy_check_mark: |
| <= 6.0 | :x: | | <= 4.0 | :x: |
## Reporting a Vulnerability ## Reporting a Vulnerability

View file

@ -127,11 +127,11 @@ ADMIN_PASSWORD needs to be set.
## APPLICATION_ROOT ## APPLICATION_ROOT
By default, ihatemoney will be served at domain root (e.g: If empty, ihatemoney will be served at domain root (e.g:
*http://domain.tld*), if set to `"/somestring"`, it will be served from a *http://domain.tld*), if set to `"somestring"`, it will be served from a
"folder" (e.g: *http://domain.tld/somestring*). "folder" (e.g: *http://domain.tld/somestring*).
- **Default value:** `"/"` - **Default value:** `""` (empty string)
## BABEL_DEFAULT_TIMEZONE ## BABEL_DEFAULT_TIMEZONE
@ -173,14 +173,6 @@ URL you want.
- **Default value:** `""` (empty string) - **Default value:** `""` (empty string)
- **Production value:** The URL of your chosing. - **Production value:** The URL of your chosing.
## SITE_NAME
It is possible to change the name of the site to something at your liking.
- **Default value:** `"I Hate Money"` (empty string)
- **Production value:** The name of your choosing
## Configuring email sending ## Configuring email sending
By default, Ihatemoney sends emails using a local SMTP server, but it's By default, Ihatemoney sends emails using a local SMTP server, but it's

View file

@ -1,37 +1,5 @@
# Contributing # Contributing
## Current direction (as of 2024)
Ihatemoney was started in 2011, and we believe the project has reached a certain
level of maturity now. The overall energy of contributors is not as high as it
used to be.
In addition, there are now several self-hosted alternatives (for instance
[cospend](https://github.com/julien-nc/cospend-nc/tree/main),
[spliit](https://github.com/spliit-app/spliit)).
As maintainers, we believe that the project is still relevant but should gear
towards some kind of "maintenance mode":
* **Simplicity** is now the main goal of the project. It has always been a compass
for the project, and the resulting software is appreciated by both users and
server administrators. For us, "simplicity" is positive and encompasses both
technical aspects (very few javascript code, manageable dependencies, small code
size...) and user-visible aspects (straightforward interface, no need to create
accounts for people you invite, same web interface on mobile...)
* **Stability** is prioritized over adding major new features. We found ourselves
complexifying the codebase (and the interface) while accepting some
contributions. Our goal now is to have a minimal set of features that do most of
the job. We believe this will help lower the maintainance burden.
* **User interface and user experience improvements** are always super welcome !
It is still possible to propose new features, but they should fit into
this new direction. Simplicity of the UI/UX and simplicity of the technical
implementation will be the main factors when deciding to accept new features.
## How to contribute ## How to contribute
You would like to contribute? First, thanks a bunch! This project is a You would like to contribute? First, thanks a bunch! This project is a
@ -78,15 +46,6 @@ Thanks again!
(setup-dev-environment)= (setup-dev-environment)=
## Set up a dev environment ## Set up a dev environment
### Requirements
In addition to general {ref}`requirements<system-requirements>`, you will need
**uv**. It recommended to install uv [system
wide](https://docs.astral.sh/uv/getting-started/installation/#standalone-installer)
as it is a kind of replacement for pip.
### Getting the sources
You must develop on top of the Git master branch: You must develop on top of the Git master branch:
git clone https://github.com/spiral-project/ihatemoney.git git clone https://github.com/spiral-project/ihatemoney.git
@ -192,7 +151,7 @@ We are using [black](https://black.readthedocs.io/en/stable/) and
Python files in this project. Be sure to run it locally on your files. Python files in this project. Be sure to run it locally on your files.
To do so, just run: To do so, just run:
make lint make black isort
You can also integrate them with your dev environment (as a You can also integrate them with your dev environment (as a
*format-on-save* hook, for instance). *format-on-save* hook, for instance).
@ -315,9 +274,10 @@ In order to issue a new release, follow the following steps:
make compress-assets make compress-assets
- Extract the translations: - Build the translations:
make extract-translations make update-translations
make build-translations
- If you're not completely sure of yourself at this point, you can - If you're not completely sure of yourself at this point, you can
optionally: create a new branch, push it, open a pull request, check optionally: create a new branch, push it, open a pull request, check

View file

@ -26,7 +26,7 @@ hub](https://hub.docker.com/r/ihatemoney/ihatemoney/).
This is probably the simplest way to get something running. Once you This is probably the simplest way to get something running. Once you
have Docker installed on your system, just issue : have Docker installed on your system, just issue :
docker run -d -p 8000:8000 ihatemoney/ihatemoney:latest docker run -d -p 8000:8000 ihatemoney/ihatemoney
Ihatemoney is now available on <http://localhost:8000>. Ihatemoney is now available on <http://localhost:8000>.
@ -54,10 +54,6 @@ To enable the Admin dashboard, first generate a hashed password with:
docker run -it --rm --entrypoint ihatemoney ihatemoney/ihatemoney generate_password_hash docker run -it --rm --entrypoint ihatemoney ihatemoney/ihatemoney generate_password_hash
:::{note}
The generated password hash is salted. Which means that the same password will generate a different hash each time. This is normal and expected behavior.
:::
At the prompt, enter a password to use for the admin dashboard. The At the prompt, enter a password to use for the admin dashboard. The
command will print the hashed password string. command will print the hashed password string.
@ -66,18 +62,12 @@ Add these additional environment variables to the docker run invocation:
-e ACTIVATE_ADMIN_DASHBOARD=True \ -e ACTIVATE_ADMIN_DASHBOARD=True \
-e ADMIN_PASSWORD=<hashed_password_string> \ -e ADMIN_PASSWORD=<hashed_password_string> \
:::{note}
If you are using a `docker-compose.yml` file and need to include a password hash, use `$$` instead of `$` to escape the dollar sign. This ensures that the hash is treated as a literal string rather than a variable in Bash.
:::
Additional gunicorn parameters can be passed using the docker `CMD` Additional gunicorn parameters can be passed using the docker `CMD`
parameter. For example, use the following command to add more gunicorn parameter. For example, use the following command to add more gunicorn
workers: workers:
docker run -d -p 8000:8000 ihatemoney/ihatemoney -w 3 docker run -d -p 8000:8000 ihatemoney/ihatemoney -w 3
If needed, there is a `docker-compose.yml` file available as an example on the [project repository](https://github.com/spiral-project/ihatemoney/blob/master/docker-compose.yml)
(cloud)= (cloud)=
## On a Cloud Provider ## On a Cloud Provider
@ -93,7 +83,7 @@ Some Paas (Platform-as-a-Service), provide a documentation or even a quick insta
«Ihatemoney» depends on: «Ihatemoney» depends on:
- **Python**: any version from 3.8 to 3.12 will work. - **Python**: any version from 3.7 to 3.11 will work.
- **A database backend**: choose among SQLite, PostgreSQL, MariaDB (>= - **A database backend**: choose among SQLite, PostgreSQL, MariaDB (>=
10.3.2). 10.3.2).
- **Virtual environment** (recommended): [python3-venv]{.title-ref} - **Virtual environment** (recommended): [python3-venv]{.title-ref}

View file

@ -1,11 +0,0 @@
import sys
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
class CustomBuildHook(BuildHookInterface):
def initialize(self, version, build_data):
sys.path.insert(0, "./ihatemoney")
from babel_utils import compile_catalogs
compile_catalogs()

View file

@ -1,2 +1,3 @@
[python: **.py] [python: **.py]
[jinja2: **/templates/**.html] [jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

View file

@ -1,11 +0,0 @@
from pathlib import Path
from babel.messages.frontend import compile_catalog
def compile_catalogs():
cmd = compile_catalog()
cmd.directory = Path(__file__).parent / "translations"
cmd.statistics = True
cmd.finalize_options()
cmd.run()

View file

@ -3,7 +3,6 @@ DEBUG = SQLACHEMY_ECHO = False
SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/ihatemoney.db" SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/ihatemoney.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = "tralala" SECRET_KEY = "tralala"
SITE_NAME = "I Hate Money"
MAIL_DEFAULT_SENDER = "Budget manager <admin@example.com>" MAIL_DEFAULT_SENDER = "Budget manager <admin@example.com>"
SHOW_ADMIN_EMAIL = True SHOW_ADMIN_EMAIL = True
ACTIVATE_DEMO_PROJECT = True ACTIVATE_DEMO_PROJECT = True
@ -15,7 +14,6 @@ APPLICATION_ROOT = "/"
ENABLE_CAPTCHA = False ENABLE_CAPTCHA = False
LEGAL_LINK = "" LEGAL_LINK = ""
SUPPORTED_LANGUAGES = [ SUPPORTED_LANGUAGES = [
"az",
"ca", "ca",
"cs", "cs",
"de", "de",

View file

@ -39,7 +39,7 @@ from wtforms.validators import (
) )
from ihatemoney.currency_convertor import CurrencyConverter from ihatemoney.currency_convertor import CurrencyConverter
from ihatemoney.models import Bill, BillType, LoggingMode, Person, Project from ihatemoney.models import Bill, LoggingMode, Person, Project
from ihatemoney.utils import ( from ihatemoney.utils import (
em_surround, em_surround,
eval_arithmetic_expression, eval_arithmetic_expression,
@ -90,6 +90,7 @@ def get_billform_for(project, set_default=True, **kwargs):
class CommaDecimalField(DecimalField): class CommaDecimalField(DecimalField):
"""A class to deal with comma in Decimal Field""" """A class to deal with comma in Decimal Field"""
def process_formdata(self, value): def process_formdata(self, value):
@ -194,7 +195,7 @@ class EditProjectForm(FlaskForm):
raise ValidationError(msg) raise ValidationError(msg)
if ( if (
project is not None project is not None
and field.data != project.default_currency and field.data != CurrencyConverter.no_currency
and project.has_bills() and project.has_bills()
): ):
msg = _( msg = _(
@ -363,12 +364,6 @@ class BillForm(FlaskForm):
payed_for = SelectMultipleField( payed_for = SelectMultipleField(
_("For whom?"), validators=[DataRequired()], coerce=int _("For whom?"), validators=[DataRequired()], coerce=int
) )
bill_type = SelectField(
_("Bill Type"),
choices=BillType.choices(),
coerce=BillType,
default=BillType.EXPENSE,
)
submit = SubmitField(_("Submit")) submit = SubmitField(_("Submit"))
submit2 = SubmitField(_("Submit and add a new one")) submit2 = SubmitField(_("Submit and add a new one"))
@ -382,14 +377,12 @@ class BillForm(FlaskForm):
payer_id=self.payer.data, payer_id=self.payer.data,
project_default_currency=project.default_currency, project_default_currency=project.default_currency,
what=self.what.data, what=self.what.data,
bill_type=self.bill_type.data,
) )
def save(self, bill, project): def save(self, bill, project):
bill.payer_id = self.payer.data bill.payer_id = self.payer.data
bill.amount = self.amount.data bill.amount = self.amount.data
bill.what = self.what.data bill.what = self.what.data
bill.bill_type = BillType(self.bill_type.data)
bill.external_link = self.external_link.data bill.external_link = self.external_link.data
bill.date = self.date.data bill.date = self.date.data
bill.owers = Person.query.get_by_ids(self.payed_for.data, project) bill.owers = Person.query.get_by_ids(self.payed_for.data, project)
@ -403,7 +396,6 @@ class BillForm(FlaskForm):
self.payer.data = bill.payer_id self.payer.data = bill.payer_id
self.amount.data = bill.amount self.amount.data = bill.amount
self.what.data = bill.what self.what.data = bill.what
self.bill_type.data = bill.bill_type
self.external_link.data = bill.external_link self.external_link.data = bill.external_link
self.original_currency.data = bill.original_currency self.original_currency.data = bill.original_currency
self.date.data = bill.date self.date.data = bill.date

View file

@ -38,10 +38,7 @@ def history_sort_key(history_item_dict):
def describe_version(version_obj): def describe_version(version_obj):
"""Use the base model str() function to describe a version object""" """Use the base model str() function to describe a version object"""
if not version_obj: return parent_class(type(version_obj)).__str__(version_obj)
return ""
else:
return parent_class(type(version_obj)).__str__(version_obj)
def describe_owers_change(version, human_readable_names): def describe_owers_change(version, human_readable_names):

View file

@ -4,7 +4,6 @@ import getpass
import os import os
import random import random
import sys import sys
import datetime
import click import click
from flask.cli import FlaskGroup from flask.cli import FlaskGroup
@ -94,31 +93,5 @@ def delete_project(project_name):
db.session.commit() db.session.commit()
@cli.command()
@click.argument("print_emails", default=False)
@click.argument("bills", default=0) # default values will get total projects
@click.argument("days", default=73000) # approximately 200 years
def get_project_count(print_emails, bills, days):
"""Count projets with at least x bills and at less than x days old"""
projects = [
pr
for pr in Project.query.all()
if pr.get_bills().count() > bills
and pr.get_bills()[0].date
> datetime.date.today() - datetime.timedelta(days=days)
]
click.secho("Number of projects: " + str(len(projects)))
if print_emails:
emails = set([pr.contact_email for pr in projects])
emails_str = ", ".join(emails)
if len(emails) > 1:
click.secho("Contact emails: " + emails_str)
elif len(emails) == 1:
click.secho("Contact email: " + emails_str)
else:
click.secho("No contact emails found")
if __name__ == "__main__": if __name__ == "__main__":
cli() cli()

View file

@ -759,7 +759,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

View file

@ -1,34 +0,0 @@
"""new bill type attribute added
Revision ID: 7a9b38559992
Revises: 927ed575acbd
Create Date: 2022-12-10 17:25:38.387643
"""
# revision identifiers, used by Alembic.
revision = "7a9b38559992"
down_revision = "927ed575acbd"
from alembic import op
import sqlalchemy as sa
from ihatemoney.models import BillType
def upgrade():
billtype_enum = sa.Enum(BillType)
billtype_enum.create(op.get_bind(), checkfirst=True)
op.add_column(
"bill",
sa.Column("bill_type", billtype_enum, server_default=BillType.EXPENSE.name),
)
op.add_column("bill_version", sa.Column("bill_type", sa.UnicodeText()))
def downgrade():
op.drop_column("bill", "bill_type")
op.drop_column("bill_version", "bill_type")
billtype_enum = sa.Enum(BillType)
billtype_enum.drop(op.get_bind())

View file

@ -1,6 +1,5 @@
from collections import defaultdict from collections import defaultdict
import datetime import datetime
from enum import Enum
import itertools import itertools
from dateutil.parser import parse from dateutil.parser import parse
@ -51,16 +50,6 @@ make_versioned(
], ],
) )
class BillType(Enum):
EXPENSE = "Expense"
REIMBURSEMENT = "Reimbursement"
@classmethod
def choices(cls):
return [(choice.value, choice.value) for choice in cls]
db = SQLAlchemy() db = SQLAlchemy()
@ -123,33 +112,22 @@ class Project(db.Model):
- dict mapping each member to how much he/she should be paid by - dict mapping each member to how much he/she should be paid by
others (i.e. how much he/she has paid for bills) others (i.e. how much he/she has paid for bills)
balance spent paid
""" """
balances, should_pay, should_receive = (defaultdict(int) for time in (1, 2, 3)) balances, should_pay, should_receive = (defaultdict(int) for time in (1, 2, 3))
for bill in self.get_bills_unordered().all(): for bill in self.get_bills_unordered().all():
should_receive[bill.payer.id] += bill.converted_amount
total_weight = sum(ower.weight for ower in bill.owers) total_weight = sum(ower.weight for ower in bill.owers)
for ower in bill.owers:
if bill.bill_type == BillType.EXPENSE: should_pay[ower.id] += (
should_receive[bill.payer.id] += bill.converted_amount ower.weight * bill.converted_amount / total_weight
for ower in bill.owers: )
should_pay[ower.id] += (
ower.weight * bill.converted_amount / total_weight
)
if bill.bill_type == BillType.REIMBURSEMENT:
should_receive[bill.payer.id] += bill.converted_amount
for ower in bill.owers:
should_receive[ower.id] -= bill.converted_amount
for person in self.members: for person in self.members:
balance = should_receive[person.id] - should_pay[person.id] balance = should_receive[person.id] - should_pay[person.id]
balances[person.id] = balance balances[person.id] = balance
return ( return balances, should_pay, should_receive
balances,
should_pay,
should_receive,
)
@property @property
def balance(self): def balance(self):
@ -182,8 +160,7 @@ class Project(db.Model):
""" """
monthly = defaultdict(lambda: defaultdict(float)) monthly = defaultdict(lambda: defaultdict(float))
for bill in self.get_bills_unordered().all(): for bill in self.get_bills_unordered().all():
if bill.bill_type == BillType.EXPENSE: monthly[bill.date.year][bill.date.month] += bill.converted_amount
monthly[bill.date.year][bill.date.month] += bill.converted_amount
return monthly return monthly
@property @property
@ -209,6 +186,7 @@ class Project(db.Model):
) )
return pretty_transactions return pretty_transactions
# cache value for better performance
members = {person.id: person for person in self.members} members = {person.id: person for person in self.members}
settle_plan = settle(self.balance.items()) or [] settle_plan = settle(self.balance.items()) or []
@ -224,6 +202,22 @@ class Project(db.Model):
return prettify(transactions, pretty_output) return prettify(transactions, pretty_output)
def exactmatch(self, credit, debts):
"""Recursively try and find subsets of 'debts' whose sum is equal to credit"""
if not debts:
return None
if debts[0]["balance"] > credit:
return self.exactmatch(credit, debts[1:])
elif debts[0]["balance"] == credit:
return [debts[0]]
else:
match = self.exactmatch(credit - debts[0]["balance"], debts[1:])
if match:
match.append(debts[0])
else:
match = self.exactmatch(credit, debts[1:])
return match
def has_bills(self): def has_bills(self):
"""return if the project do have bills or not""" """return if the project do have bills or not"""
return self.get_bills_unordered().count() > 0 return self.get_bills_unordered().count() > 0
@ -342,7 +336,6 @@ class Project(db.Model):
pretty_bills.append( pretty_bills.append(
{ {
"what": bill.what, "what": bill.what,
"bill_type": bill.bill_type.value,
"amount": round(bill.amount, 2), "amount": round(bill.amount, 2),
"currency": bill.original_currency, "currency": bill.original_currency,
"date": str(bill.date), "date": str(bill.date),
@ -414,7 +407,6 @@ class Project(db.Model):
new_bill = Bill( new_bill = Bill(
amount=b["amount"], amount=b["amount"],
date=parse(b["date"]), date=parse(b["date"]),
bill_type=b["bill_type"],
external_link="", external_link="",
original_currency=b["currency"], original_currency=b["currency"],
owers=Person.query.get_by_names(b["owers"], self), owers=Person.query.get_by_names(b["owers"], self),
@ -545,15 +537,14 @@ class Project(db.Model):
db.session.commit() db.session.commit()
operations = ( operations = (
("Georg", 200, ("Amina", "Georg", "Alice"), "Food shopping", "Expense"), ("Georg", 200, ("Amina", "Georg", "Alice"), "Food shopping"),
("Alice", 20, ("Amina", "Alice"), "Beer !", "Expense"), ("Alice", 20, ("Amina", "Alice"), "Beer !"),
("Amina", 50, ("Amina", "Alice", "Georg"), "AMAP", "Expense"), ("Amina", 50, ("Amina", "Alice", "Georg"), "AMAP"),
) )
for payer, amount, owers, what, bill_type in operations: for payer, amount, owers, what in operations:
db.session.add( db.session.add(
Bill( Bill(
amount=amount, amount=amount,
bill_type=bill_type,
original_currency=project.default_currency, original_currency=project.default_currency,
owers=[members[name] for name in owers], owers=[members[name] for name in owers],
payer_id=members[payer].id, payer_id=members[payer].id,
@ -686,7 +677,6 @@ class Bill(db.Model):
date = db.Column(db.Date, default=datetime.datetime.now) date = db.Column(db.Date, default=datetime.datetime.now)
creation_date = db.Column(db.Date, default=datetime.datetime.now) creation_date = db.Column(db.Date, default=datetime.datetime.now)
what = db.Column(db.UnicodeText) what = db.Column(db.UnicodeText)
bill_type = db.Column(db.Enum(BillType))
external_link = db.Column(db.UnicodeText) external_link = db.Column(db.UnicodeText)
original_currency = db.Column(db.String(3)) original_currency = db.Column(db.String(3))
@ -706,7 +696,6 @@ class Bill(db.Model):
payer_id: int = None, payer_id: int = None,
project_default_currency: str = "", project_default_currency: str = "",
what: str = "", what: str = "",
bill_type: str = "Expense",
): ):
super().__init__() super().__init__()
self.amount = amount self.amount = amount
@ -716,7 +705,6 @@ class Bill(db.Model):
self.owers = owers self.owers = owers
self.payer_id = payer_id self.payer_id = payer_id
self.what = what self.what = what
self.bill_type = BillType(bill_type)
self.converted_amount = self.currency_helper.exchange_currency( self.converted_amount = self.currency_helper.exchange_currency(
self.amount, self.original_currency, project_default_currency self.amount, self.original_currency, project_default_currency
) )
@ -731,7 +719,6 @@ class Bill(db.Model):
"date": self.date, "date": self.date,
"creation_date": self.creation_date, "creation_date": self.creation_date,
"what": self.what, "what": self.what,
"bill_type": self.bill_type.value,
"external_link": self.external_link, "external_link": self.external_link,
"original_currency": self.original_currency, "original_currency": self.original_currency,
"converted_amount": self.converted_amount, "converted_amount": self.converted_amount,

View file

@ -103,7 +103,7 @@ def validate_configuration(app):
if "MAIL_DEFAULT_SENDER" not in app.config: if "MAIL_DEFAULT_SENDER" not in app.config:
app.config["MAIL_DEFAULT_SENDER"] = default_settings.DEFAULT_MAIL_SENDER app.config["MAIL_DEFAULT_SENDER"] = default_settings.DEFAULT_MAIL_SENDER
if type(app.config["MAIL_DEFAULT_SENDER"]) is tuple: if type(app.config["MAIL_DEFAULT_SENDER"]) == tuple:
(name, address) = app.config["MAIL_DEFAULT_SENDER"] (name, address) = app.config["MAIL_DEFAULT_SENDER"]
app.config["MAIL_DEFAULT_SENDER"] = f"{name} <{address}>" app.config["MAIL_DEFAULT_SENDER"] = f"{name} <{address}>"
warnings.warn( warnings.warn(

View file

@ -200,7 +200,6 @@
{% if g.project.default_currency != "XXX" %} {% if g.project.default_currency != "XXX" %}
{{ input(form.original_currency, inline=True, class="form-control custom-select") }} {{ input(form.original_currency, inline=True, class="form-control custom-select") }}
{% endif %} {% endif %}
{{ input(form.bill_type, inline=True) }}
{{ input(form.external_link, inline=True) }} {{ input(form.external_link, inline=True) }}
</details> </details>
</fieldset> </fieldset>

View file

@ -12,7 +12,7 @@
{{ _("or") }} {{ _("or") }}
<span class="side-to-side"> <span class="side-to-side">
<a class="showcase btn" <a class="showcase btn"
onclick="javascript:showGallery(); return false;">{{ _("See the explanatory comics") }}</a> onclick="javascript:showGallery(); return false;">{{ _("See the explicative comics") }}</a>
<img class="showcaseimg" <img class="showcaseimg"
src="{{ url_for("static", filename='images/indicate.svg') }}" /> src="{{ url_for("static", filename='images/indicate.svg') }}" />
</span> </span>

View file

@ -20,7 +20,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="h-100"> <html class="h-100">
<head> <head>
<title>{{ SITE_NAME }} — {{ _("Account manager") }}{% block title %}{% endblock %}</title> <title>{{ _("Account manager") }}{% block title %}{% endblock %}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8"> <meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel=stylesheet type=text/css href="{{ url_for("static", filename='css/main.css') }}"> <link rel=stylesheet type=text/css href="{{ url_for("static", filename='css/main.css') }}">
@ -168,7 +168,7 @@
<i class="icon book">{{ static_include("images/book.svg") | safe }}</i> <i class="icon book">{{ static_include("images/book.svg") | safe }}</i>
</a> </a>
{% if g.show_admin_dashboard_link %} {% if g.show_admin_dashboard_link %}
<a target="_blank" rel="noopener" data-toggle="tooltip" data-placement="top" title="{{ _('Administration Dashboard') }}" href="{{ url_for('main.dashboard') }}"> <a target="_blank" rel="noopener" data-toggle="tooltip" data-placement="top" title="{{ _('Administation Dashboard') }}" href="{{ url_for('main.dashboard') }}">
<i class="icon admin">{{ static_include("images/cog.svg") | safe }}</i> <i class="icon admin">{{ static_include("images/cog.svg") | safe }}</i>
</a> </a>
{% endif %} {% endif %}

View file

@ -64,10 +64,10 @@
</div> </div>
</div> </div>
<div class="identifier"> <div class="identifier">
{% if display_showcase %} {% if g.lang == 'fr' %}
<a class="btn btn-secondary btn-block" href="" onclick="javascript:showGallery(); return false;"> <a class="btn btn-secondary btn-block" href="" onclick="javascript:showGallery(); return false;">
<i class="icon icon-white high before-text">{{ static_include("images/read.svg") | safe }}</i> <i class="icon icon-white high before-text">{{ static_include("images/read.svg") | safe }}</i>
{{ _("See the explanatory comics") }} Voir la BD explicative
</a> </a>
{% endif %} {% endif %}
<a class="btn btn-secondary btn-block" href="{{ url_for('.invite') }}"> <a class="btn btn-secondary btn-block" href="{{ url_for('.invite') }}">

View file

@ -9,22 +9,13 @@
{% block content %} {% block content %}
<table id="bill_table" class="split_bills table table-striped"> <table id="bill_table" class="split_bills table table-striped">
<thead><tr><th>{{ _("Who pays?") }}</th><th>{{ _("To whom?") }}</th><th>{{ _("How much?") }}</th><th>{{ _("Settled?") }}</th></tr></thead> <thead><tr><th>{{ _("Who pays?") }}</th><th>{{ _("To whom?") }}</th><th>{{ _("How much?") }}</th></tr></thead>
<tbody> <tbody>
{% for bill in bills %} {% for bill in bills %}
<tr receiver={{bill.receiver.id}}> <tr receiver={{bill.receiver.id}}>
<td>{{ bill.ower }}</td> <td>{{ bill.ower }}</td>
<td>{{ bill.receiver }}</td> <td>{{ bill.receiver }}</td>
<td>{{ bill.amount|currency }}</td> <td>{{ bill.amount|currency }}</td>
<td>
<span id="settle-bill" class="ml-auto pb-2">
<a href="{{ url_for('.settle', amount = bill.amount, ower_id = bill.ower.id, payer_id = bill.receiver.id) }}" class="btn btn-primary">
<div data-toggle="tooltip" title='{{ _("Click here to record that the money transfer has been done") }}'>
{{ ("Settle") }}
</div>
</a>
</span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View file

@ -13,7 +13,7 @@
<div class="pswp__ui pswp__ui--hidden"> <div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar"> <div class="pswp__top-bar">
<div class="pswp__counter"></div> <div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="{{ _("Close (Esc)") }}"> <button class="pswp__button pswp__button--close" title="Fermer (Esc)"></button>
<div class="pswp__preloader"> <div class="pswp__preloader">
<div class="pswp__preloader__icn"> <div class="pswp__preloader__icn">
<div class="pswp__preloader__cut"> <div class="pswp__preloader__cut">
@ -23,9 +23,9 @@
</div> </div>
</div> </div>
<button class="pswp__button pswp__button--arrow--left" <button class="pswp__button pswp__button--arrow--left"
title="{{ _("Next (right arrow)") }}"></button> title="Suivant (flèche droite)"></button>
<button class="pswp__button pswp__button--arrow--right" <button class="pswp__button pswp__button--arrow--right"
title="{{ _("Previous (left arrow)") }}"></button> title="Précédent (flèche gauche)"></button>
<div class="pswp__caption"> <div class="pswp__caption">
<div class="pswp__caption__center"></div> <div class="pswp__caption__center"></div>
</div> </div>

View file

@ -11,7 +11,7 @@
</tr> </tr>
</thead> </thead>
{%- endif %} {%- endif %}
{%- for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2)|abs > 0.01 %} {%- for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %}
<tr id="bal-member-{{ member.id }}" action="{% if member.activated %}delete{% else %}reactivate{% endif %}"> <tr id="bal-member-{{ member.id }}" action="{% if member.activated %}delete{% else %}reactivate{% endif %}">
<td class="balance-name">{{ member.name }} <td class="balance-name">{{ member.name }}
{%- if show_weight -%} {%- if show_weight -%}
@ -61,4 +61,4 @@
{% endblock %} {% endblock %}
{# It must be set outside of the block definition #} {# It must be set outside of the block definition #}
{% set messages_shown = True %} {% set messages_shown = True %}

View file

@ -9,6 +9,7 @@ from ihatemoney.tests.common.ihatemoney_testcase import IhatemoneyTestCase
class TestAPI(IhatemoneyTestCase): class TestAPI(IhatemoneyTestCase):
"""Tests the API""" """Tests the API"""
def api_create( def api_create(
@ -42,7 +43,7 @@ class TestAPI(IhatemoneyTestCase):
def get_auth(self, username, password=None): def get_auth(self, username, password=None):
password = password or username password = password or username
base64string = ( base64string = (
base64.encodebytes(f"{username}:{password}".encode("utf-8")) # noqa: E231 base64.encodebytes(f"{username}:{password}".encode("utf-8"))
.decode("utf-8") .decode("utf-8")
.replace("\n", "") .replace("\n", "")
) )
@ -406,7 +407,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
}, },
@ -431,7 +431,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1},
{"activated": True, "id": 2, "name": "jeanne", "weight": 1}, {"activated": True, "id": 2, "name": "jeanne", "weight": 1},
], ],
"bill_type": "Expense",
"amount": 25.0, "amount": 25.0,
"date": "2011-08-10", "date": "2011-08-10",
"id": 1, "id": 1,
@ -463,7 +462,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
}, },
@ -481,7 +479,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "beer", "what": "beer",
"payer": "2", "payer": "2",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
}, },
@ -503,7 +500,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1},
{"activated": True, "id": 2, "name": "jeanne", "weight": 1}, {"activated": True, "id": 2, "name": "jeanne", "weight": 1},
], ],
"bill_type": "Expense",
"amount": 25.0, "amount": 25.0,
"date": "2011-09-10", "date": "2011-09-10",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
@ -558,7 +554,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": input_amount, "amount": input_amount,
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
@ -583,7 +578,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1},
{"activated": True, "id": 2, "name": "jeanne", "weight": 1}, {"activated": True, "id": 2, "name": "jeanne", "weight": 1},
], ],
"bill_type": "Expense",
"amount": expected_amount, "amount": expected_amount,
"date": "2011-08-10", "date": "2011-08-10",
"id": id, "id": id,
@ -617,7 +611,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": amount, "amount": amount,
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
@ -665,7 +658,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
}, },
@ -690,7 +682,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1},
{"activated": True, "id": 2, "name": "jeanne", "weight": 1}, {"activated": True, "id": 2, "name": "jeanne", "weight": 1},
], ],
"bill_type": "Expense",
"amount": 25.0, "amount": 25.0,
"date": "2011-08-10", "date": "2011-08-10",
"id": 1, "id": 1,
@ -715,7 +706,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "30", "amount": "30",
"external_link": "https://raclette.fr", "external_link": "https://raclette.fr",
"original_currency": "CAD", "original_currency": "CAD",
@ -737,7 +727,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1.0}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1.0},
{"activated": True, "id": 2, "name": "jeanne", "weight": 1.0}, {"activated": True, "id": 2, "name": "jeanne", "weight": 1.0},
], ],
"bill_type": "Expense",
"amount": 30.0, "amount": 30.0,
"date": "2011-08-10", "date": "2011-08-10",
"id": 1, "id": 1,
@ -758,7 +747,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "Pierogi", "what": "Pierogi",
"payer": "1", "payer": "1",
"payed_for": ["2", "3"], "payed_for": ["2", "3"],
"bill_type": "Expense",
"amount": "80", "amount": "80",
"original_currency": "PLN", "original_currency": "PLN",
}, },
@ -803,7 +791,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
@ -868,7 +855,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1", "2"], "payed_for": ["1", "2"],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
@ -891,7 +877,6 @@ class TestAPI(IhatemoneyTestCase):
{"activated": True, "id": 1, "name": "zorglub", "weight": 1}, {"activated": True, "id": 1, "name": "zorglub", "weight": 1},
{"activated": True, "id": 2, "name": "jeannedy familly", "weight": 4}, {"activated": True, "id": 2, "name": "jeannedy familly", "weight": 4},
], ],
"bill_type": "Expense",
"amount": 25.0, "amount": 25.0,
"date": "2011-08-10", "date": "2011-08-10",
"id": 1, "id": 1,
@ -977,7 +962,6 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1"], "payed_for": ["1"],
"bill_type": "Expense",
"amount": "0", "amount": "0",
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
@ -1006,71 +990,8 @@ class TestAPI(IhatemoneyTestCase):
"what": "fromage", "what": "fromage",
"payer": "1", "payer": "1",
"payed_for": ["1"], "payed_for": ["1"],
"bill_type": "Expense",
"amount": "9347242149381274732472348728748723473278472843.12", "amount": "9347242149381274732472348728748723473278472843.12",
}, },
headers=self.get_auth("raclette"), headers=self.get_auth("raclette"),
) )
self.assertStatus(400, req) self.assertStatus(400, req)
def test_validate_bill_type(self):
self.api_create("raclette")
self.api_add_member("raclette", "zorglub")
req = self.client.post(
"/api/projects/raclette/bills",
data={
"date": "2011-08-10",
"what": "fromage",
"payer": "1",
"payed_for": ["1"],
"bill_type": "wrong_bill_type",
"amount": "50",
},
headers=self.get_auth("raclette"),
)
self.assertStatus(400, req)
req = self.client.post(
"/api/projects/raclette/bills",
data={
"date": "2011-08-10",
"what": "fromage",
"payer": "1",
"payed_for": ["1"],
"bill_type": "Expense",
"amount": "50",
},
headers=self.get_auth("raclette"),
)
self.assertStatus(201, req)
def test_default_bill_type(self):
self.api_create("raclette")
self.api_add_member("raclette", "zorglub")
# Post a bill without adding a bill type
req = self.client.post(
"/api/projects/raclette/bills",
data={
"date": "2011-08-10",
"what": "fromage",
"payer": "1",
"payed_for": ["1"],
"amount": "50",
},
headers=self.get_auth("raclette"),
)
self.assertStatus(201, req)
req = self.client.get(
"/api/projects/raclette/bills/1", headers=self.get_auth("raclette")
)
self.assertStatus(200, req)
# Bill type should now be "Expense"
got = json.loads(req.data.decode("utf-8"))
assert got["bill_type"] == "Expense"

View file

@ -1,9 +1,10 @@
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta, date import datetime
import re import re
from urllib.parse import unquote, urlparse, urlunparse from urllib.parse import unquote, urlparse, urlunparse
from flask import session, url_for from flask import session, url_for
from libfaketime import fake_time
import pytest import pytest
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
@ -238,10 +239,7 @@ class TestBudget(IhatemoneyTestCase):
url, data={"password": "pass", "password_confirmation": "pass"} url, data={"password": "pass", "password_confirmation": "pass"}
) )
resp = self.login("raclette", password="pass") resp = self.login("raclette", password="pass")
assert ( assert "<title>Account manager - raclette</title>" in resp.data.decode("utf-8")
"<title>I Hate Money — Account manager - raclette</title>"
in resp.data.decode("utf-8")
)
# Test empty and null tokens # Test empty and null tokens
resp = self.client.get("/reset-password") resp = self.client.get("/reset-password")
assert "No token provided" in resp.data.decode("utf-8") assert "No token provided" in resp.data.decode("utf-8")
@ -430,7 +428,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": jeanne_id, "payer": jeanne_id,
"payed_for": [jeanne_id], "payed_for": [jeanne_id],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
) )
@ -482,7 +479,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": zorglub.id, "payer": zorglub.id,
"payed_for": [zorglub.id], "payed_for": [zorglub.id],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
) )
@ -650,7 +646,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
) )
@ -666,7 +661,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -690,7 +684,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "19", "amount": "19",
}, },
) )
@ -702,7 +695,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[1], "payer": members_ids[1],
"payed_for": members_ids[0], "payed_for": members_ids[0],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -714,7 +706,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[1], "payer": members_ids[1],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "17", "amount": "17",
}, },
) )
@ -730,7 +721,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "-25", "amount": "-25",
}, },
) )
@ -745,7 +735,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "25,02", "amount": "25,02",
}, },
) )
@ -760,7 +749,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "42", "amount": "42",
"external_link": "https://example.com/fromage", "external_link": "https://example.com/fromage",
}, },
@ -776,63 +764,12 @@ class TestBudget(IhatemoneyTestCase):
"what": "mauvais fromage à raclette", "what": "mauvais fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "42000", "amount": "42000",
"external_link": "javascript:alert('Tu bluffes, Martoni.')", "external_link": "javascript:alert('Tu bluffes, Martoni.')",
}, },
) )
assert "Invalid URL" in resp.data.decode("utf-8") assert "Invalid URL" in resp.data.decode("utf-8")
def test_reimbursement_bill(self):
self.post_project("rent")
# add two participants
self.client.post("/rent/members/add", data={"name": "bob"})
self.client.post("/rent/members/add", data={"name": "alice"})
members_ids = [m.id for m in self.get_project("rent").members]
# create a bill to test reimbursement
self.client.post(
"/rent/add",
data={
"date": "2022-12-12",
"what": "december rent",
"payer": members_ids[0], # bob
"payed_for": members_ids, # bob and alice
"bill_type": "Expense",
"amount": "1000",
},
)
# check balance
balance = self.get_project("rent").balance
assert set(balance.values()), set([500 == -500])
# check paid
bob_paid = self.get_project("rent").full_balance[2][members_ids[0]]
alice_paid = self.get_project("rent").full_balance[2][members_ids[1]]
assert bob_paid == 1000
assert alice_paid == 0
# test reimbursement bill
self.client.post(
"/rent/add",
data={
"date": "2022-12-13",
"what": "reimbursement for rent",
"payer": members_ids[1], # alice
"payed_for": members_ids[0], # bob
"bill_type": "Reimbursement",
"amount": "500",
},
)
balance = self.get_project("rent").balance
assert set(balance.values()), set([0 == 0])
# check paid
bob_paid = self.get_project("rent").full_balance[2][members_ids[0]]
alice_paid = self.get_project("rent").full_balance[2][members_ids[1]]
assert bob_paid == 500
assert alice_paid == 500
def test_weighted_balance(self): def test_weighted_balance(self):
self.post_project("raclette") self.post_project("raclette")
@ -852,7 +789,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": members_ids[0], "payer": members_ids[0],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -864,7 +800,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "pommes de terre", "what": "pommes de terre",
"payer": members_ids[1], "payer": members_ids[1],
"payed_for": members_ids, "payed_for": members_ids,
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -929,7 +864,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "24.36", "amount": "24.36",
}, },
) )
@ -941,7 +875,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "19.12", "amount": "19.12",
}, },
) )
@ -953,7 +886,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "delicatessen", "what": "delicatessen",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "22", "amount": "22",
}, },
) )
@ -1085,7 +1017,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -1097,7 +1028,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -1109,7 +1039,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "delicatessen", "what": "delicatessen",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -1146,7 +1075,7 @@ class TestBudget(IhatemoneyTestCase):
assert re.search(re.compile(regex2, re.DOTALL), response.data.decode("utf-8")) assert re.search(re.compile(regex2, re.DOTALL), response.data.decode("utf-8"))
# Check monthly expenses again: it should have a single month and the correct amount # Check monthly expenses again: it should have a single month and the correct amount
august = date(year=2011, month=8, day=1) august = datetime.date(year=2011, month=8, day=1)
assert project.active_months_range() == [august] assert project.active_months_range() == [august]
assert dict(project.monthly_stats[2011]) == {8: 40.0} assert dict(project.monthly_stats[2011]) == {8: 40.0}
@ -1158,16 +1087,15 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 2, "payer": 2,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "30", "amount": "30",
}, },
) )
months = [ months = [
date(year=2011, month=12, day=1), datetime.date(year=2011, month=12, day=1),
date(year=2011, month=11, day=1), datetime.date(year=2011, month=11, day=1),
date(year=2011, month=10, day=1), datetime.date(year=2011, month=10, day=1),
date(year=2011, month=9, day=1), datetime.date(year=2011, month=9, day=1),
date(year=2011, month=8, day=1), datetime.date(year=2011, month=8, day=1),
] ]
amounts_2011 = { amounts_2011 = {
12: 30.0, 12: 30.0,
@ -1184,7 +1112,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "ice cream", "what": "ice cream",
"payer": 2, "payer": 2,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -1200,7 +1127,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "champomy", "what": "champomy",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -1216,11 +1142,10 @@ class TestBudget(IhatemoneyTestCase):
"what": "smoothie", "what": "smoothie",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
months.append(date(year=2011, month=7, day=1)) months.append(datetime.date(year=2011, month=7, day=1))
amounts_2011[7] = 20.0 amounts_2011[7] = 20.0
assert project.active_months_range() == months assert project.active_months_range() == months
assert dict(project.monthly_stats[2011]) == amounts_2011 assert dict(project.monthly_stats[2011]) == amounts_2011
@ -1233,11 +1158,10 @@ class TestBudget(IhatemoneyTestCase):
"what": "more champomy", "what": "more champomy",
"payer": 2, "payer": 2,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "30", "amount": "30",
}, },
) )
months.insert(0, date(year=2012, month=1, day=1)) months.insert(0, datetime.date(year=2012, month=1, day=1))
amounts_2012 = {1: 30.0} amounts_2012 = {1: 30.0}
assert project.active_months_range() == months assert project.active_months_range() == months
assert dict(project.monthly_stats[2011]) == amounts_2011 assert dict(project.monthly_stats[2011]) == amounts_2011
@ -1266,7 +1190,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -1278,7 +1201,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -1290,7 +1212,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "delicatessen", "what": "delicatessen",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -1306,76 +1227,6 @@ class TestBudget(IhatemoneyTestCase):
assert abs(a - balance[m.id]) < 0.01 assert abs(a - balance[m.id]) < 0.01
return return
def test_settle_button(self):
self.post_project("raclette")
# add participants
self.client.post("/raclette/members/add", data={"name": "zorglub"})
self.client.post("/raclette/members/add", data={"name": "jeanne"})
self.client.post("/raclette/members/add", data={"name": "tata"})
# Add a participant with a balance at 0 :
self.client.post("/raclette/members/add", data={"name": "pépé"})
# create bills
self.client.post(
"/raclette/add",
data={
"date": "2011-08-10",
"what": "fromage à raclette",
"payer": 1,
"payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0",
},
)
self.client.post(
"/raclette/add",
data={
"date": "2011-08-10",
"what": "red wine",
"payer": 2,
"payed_for": [1],
"bill_type": "Expense",
"amount": "20",
},
)
self.client.post(
"/raclette/add",
data={
"date": "2011-08-10",
"what": "delicatessen",
"payer": 1,
"payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10",
},
)
project = self.get_project("raclette")
transactions = project.get_transactions_to_settle_bill()
count = 0
for t in transactions:
count += 1
self.client.get(
"/raclette/settle"
+ "/"
+ str(t["amount"])
+ "/"
+ str(t["ower"].id)
+ "/"
+ str(t["receiver"].id)
)
temp_transactions = project.get_transactions_to_settle_bill()
# test if the one has disappeared
assert len(temp_transactions) == len(transactions) - count
# test if theres a new one with bill_type reimbursement
bill = project.get_newest_bill()
assert bill.bill_type == models.BillType.REIMBURSEMENT
return
def test_settle_zero(self): def test_settle_zero(self):
self.post_project("raclette") self.post_project("raclette")
@ -1392,7 +1243,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -1404,7 +1254,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1, 3], "payed_for": [1, 3],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -1416,7 +1265,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "refund", "what": "refund",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
"bill_type": "Expense",
"amount": "13.33", "amount": "13.33",
}, },
) )
@ -1449,7 +1297,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3, 4], "payed_for": [1, 2, 3, 4],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -1468,7 +1315,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "roblochon", "what": "roblochon",
"payer": 2, "payer": 2,
"payed_for": [1, 3, 4], "payed_for": [1, 3, 4],
"bill_type": "Expense",
"amount": "100.0", "amount": "100.0",
} }
# Try to access bill of another project # Try to access bill of another project
@ -1574,7 +1420,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -1586,7 +1431,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1, 3], "payed_for": [1, 3],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -1598,7 +1442,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "refund", "what": "refund",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
"bill_type": "Expense",
"amount": "13.33", "amount": "13.33",
}, },
) )
@ -1624,7 +1467,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "refund from EUR", "what": "refund from EUR",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
"bill_type": "Expense",
"amount": "20", "amount": "20",
"original_currency": "EUR", "original_currency": "EUR",
}, },
@ -1648,7 +1490,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "Poutine", "what": "Poutine",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
"bill_type": "Expense",
"amount": "18", "amount": "18",
"original_currency": "CAD", "original_currency": "CAD",
}, },
@ -1705,7 +1546,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
"original_currency": "EUR", "original_currency": "EUR",
}, },
@ -1741,7 +1581,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
"original_currency": "EUR", "original_currency": "EUR",
}, },
@ -1754,7 +1593,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "aspirine", "what": "aspirine",
"payer": 2, "payer": 2,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "5.0", "amount": "5.0",
"original_currency": "EUR", "original_currency": "EUR",
}, },
@ -1789,7 +1627,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "0", "amount": "0",
"original_currency": "XXX", "original_currency": "XXX",
}, },
@ -1834,7 +1671,6 @@ class TestBudget(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "9347242149381274732472348728748723473278472843.12", "amount": "9347242149381274732472348728748723473278472843.12",
"original_currency": "EUR", "original_currency": "EUR",
}, },
@ -1870,56 +1706,56 @@ class TestBudget(IhatemoneyTestCase):
""" """
Tests that the RSS feed output content is expected. Tests that the RSS feed output content is expected.
""" """
self.post_project("raclette", default_currency="EUR") with fake_time("2023-07-25 12:00:00"):
self.client.post("/raclette/members/add", data={"name": "george"}) self.post_project("raclette", default_currency="EUR")
self.client.post("/raclette/members/add", data={"name": "peter"}) self.client.post("/raclette/members/add", data={"name": "george"})
self.client.post("/raclette/members/add", data={"name": "steven"}) self.client.post("/raclette/members/add", data={"name": "peter"})
self.client.post("/raclette/members/add", data={"name": "steven"})
self.client.post( self.client.post(
"/raclette/add", "/raclette/add",
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"amount": "12", "amount": "12",
"original_currency": "EUR", "original_currency": "EUR",
"bill_type": "Expense", },
}, )
) self.client.post(
self.client.post( "/raclette/add",
"/raclette/add", data={
data={ "date": "2016-12-30",
"date": "2016-12-30", "what": "charcuterie",
"what": "charcuterie", "payer": 2,
"payer": 2, "payed_for": [1, 2],
"payed_for": [1, 2], "amount": "15",
"amount": "15", "original_currency": "EUR",
"original_currency": "EUR", },
"bill_type": "Expense", )
}, self.client.post(
) "/raclette/add",
self.client.post( data={
"/raclette/add", "date": "2016-12-29",
data={ "what": "vin blanc",
"date": "2016-12-29", "payer": 2,
"what": "vin blanc", "payed_for": [1, 2],
"payer": 2, "amount": "10",
"payed_for": [1, 2], "original_currency": "EUR",
"amount": "10", },
"original_currency": "EUR", )
"bill_type": "Expense",
},
)
project = self.get_project("raclette") project = self.get_project("raclette")
token = project.generate_token("feed") token = project.generate_token("feed")
resp = self.client.get(f"/raclette/feed/{token}.xml") resp = self.client.get(f"/raclette/feed/{token}.xml")
content = resp.data.decode() expected_rss_content = f"""<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
assert ( xmlns:dc="http://purl.org/dc/elements/1.1/"
f"""<channel> xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title>I Hate Money raclette</title> <title>I Hate Money raclette</title>
<description>Latest bills from raclette</description> <description>Latest bills from raclette</description>
<atom:link href="http://localhost/raclette/feed/{token}.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://localhost/raclette/feed/{token}.xml" rel="self" type="application/rss+xml" />
@ -1929,151 +1765,186 @@ class TestBudget(IhatemoneyTestCase):
<guid isPermaLink="false">1</guid> <guid isPermaLink="false">1</guid>
<dc:creator>george</dc:creator> <dc:creator>george</dc:creator>
<description>December 31, 2016 - george, peter, steven : 4.00</description> <description>December 31, 2016 - george, peter, steven : 4.00</description>
""" <pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
in content </item>
) <item>
<title>charcuterie - 15.00</title>
assert """<title>charcuterie - €15.00</title>""" in content <guid isPermaLink="false">2</guid>
assert """<title>vin blanc - €10.00</title>""" in content <dc:creator>peter</dc:creator>
<description>December 30, 2016 - george, peter : 7.50</description>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
</item>
<item>
<title>vin blanc - 10.00</title>
<guid isPermaLink="false">3</guid>
<dc:creator>peter</dc:creator>
<description>December 29, 2016 - george, peter : 5.00</description>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
</item>
</channel>
</rss>""" # noqa: E501
assert resp.data.decode() == expected_rss_content
def test_rss_feed_history_disabled(self): def test_rss_feed_history_disabled(self):
""" """
Tests that RSS feeds is correctly rendered even if the project Tests that RSS feeds is correctly rendered even if the project
history is disabled. history is disabled.
""" """
self.post_project("raclette", default_currency="EUR", project_history=False) with fake_time("2023-07-25 12:00:00"):
self.client.post("/raclette/members/add", data={"name": "george"}) self.post_project("raclette", default_currency="EUR", project_history=False)
self.client.post("/raclette/members/add", data={"name": "peter"}) self.client.post("/raclette/members/add", data={"name": "george"})
self.client.post("/raclette/members/add", data={"name": "steven"}) self.client.post("/raclette/members/add", data={"name": "peter"})
self.client.post("/raclette/members/add", data={"name": "steven"})
self.client.post( self.client.post(
"/raclette/add", "/raclette/add",
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"amount": "12", "amount": "12",
"original_currency": "EUR", "original_currency": "EUR",
"bill_type": "Expense", },
}, )
) self.client.post(
self.client.post( "/raclette/add",
"/raclette/add", data={
data={ "date": "2016-12-30",
"date": "2016-12-30", "what": "charcuterie",
"what": "charcuterie", "payer": 2,
"payer": 2, "payed_for": [1, 2],
"payed_for": [1, 2], "amount": "15",
"amount": "15", "original_currency": "EUR",
"original_currency": "EUR", },
"bill_type": "Expense", )
}, self.client.post(
) "/raclette/add",
self.client.post( data={
"/raclette/add", "date": "2016-12-29",
data={ "what": "vin blanc",
"date": "2016-12-29", "payer": 2,
"what": "vin blanc", "payed_for": [1, 2],
"payer": 2, "amount": "10",
"payed_for": [1, 2], "original_currency": "EUR",
"amount": "10", },
"original_currency": "EUR", )
"bill_type": "Expense",
},
)
project = self.get_project("raclette") project = self.get_project("raclette")
token = project.generate_token("feed") token = project.generate_token("feed")
resp = self.client.get(f"/raclette/feed/{token}.xml") resp = self.client.get(f"/raclette/feed/{token}.xml")
content = resp.data.decode() expected_rss_content = f"""<?xml version="1.0" encoding="utf-8"?>
assert """<title>charcuterie - €15.00</title>""" in content <rss version="2.0"
assert """<title>vin blanc - €10.00</title>""" in content xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title>I Hate Money raclette</title>
<description>Latest bills from raclette</description>
<atom:link href="http://localhost/raclette/feed/{token}.xml" rel="self" type="application/rss+xml" />
<link>http://localhost/raclette/</link>
<item>
<title>fromage à raclette - 12.00</title>
<guid isPermaLink="false">1</guid>
<dc:creator>george</dc:creator>
<description>December 31, 2016 - george, peter, steven : 4.00</description>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
</item>
<item>
<title>charcuterie - 15.00</title>
<guid isPermaLink="false">2</guid>
<dc:creator>peter</dc:creator>
<description>December 30, 2016 - george, peter : 7.50</description>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
</item>
<item>
<title>vin blanc - 10.00</title>
<guid isPermaLink="false">3</guid>
<dc:creator>peter</dc:creator>
<description>December 29, 2016 - george, peter : 5.00</description>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
</item>
</channel>
</rss>""" # noqa: E501
assert resp.data.decode() == expected_rss_content
def test_rss_if_modified_since_header(self): def test_rss_if_modified_since_header(self):
# Project creation # Project creation
self.post_project("raclette") with fake_time("2023-07-26 13:00:00"):
self.client.post("/raclette/members/add", data={"name": "george"}) self.post_project("raclette")
project = self.get_project("raclette") self.client.post("/raclette/members/add", data={"name": "george"})
token = project.generate_token("feed") project = self.get_project("raclette")
token = project.generate_token("feed")
resp = self.client.get(f"/raclette/feed/{token}.xml") resp = self.client.get(f"/raclette/feed/{token}.xml")
assert resp.status_code == 200 assert resp.status_code == 200
assert "Last-Modified" in resp.headers.keys() assert resp.headers.get("Last-Modified") == "Wed, 26 Jul 2023 13:00:00 UTC"
last_modified = resp.headers.get("Last-Modified")
# Get a date 1 hour before the last modified date
before = datetime.strptime(
last_modified, "%a, %d %b %Y %H:%M:%S %Z"
) - timedelta(hours=1)
before_str = before.strftime("%a, %d %b %Y %H:%M:%S %Z")
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={"If-Modified-Since": before_str}, headers={"If-Modified-Since": "Tue, 26 Jul 2023 12:00:00 UTC"},
) )
assert resp.status_code == 200 assert resp.status_code == 200
after = datetime.strptime(
last_modified, "%a, %d %b %Y %H:%M:%S %Z"
) + timedelta(hours=1)
after_str = after.strftime("%a, %d %b %Y %H:%M:%S %Z")
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={"If-Modified-Since": after_str}, headers={"If-Modified-Since": "Tue, 26 Jul 2023 14:00:00 UTC"},
) )
assert resp.status_code == 304 assert resp.status_code == 304
# Add bill # Add bill
self.login("raclette") with fake_time("2023-07-27 13:00:00"):
resp = self.client.post( self.login("raclette")
"/raclette/add", resp = self.client.post(
data={ "/raclette/add",
"date": "2016-12-31", data={
"what": "fromage à raclette", "date": "2016-12-31",
"payer": 1, "what": "fromage à raclette",
"payed_for": [1], "payer": 1,
"amount": "12", "payed_for": [1],
"original_currency": "XXX", "amount": "12",
"bill_type": "Expense", "original_currency": "XXX",
}, },
follow_redirects=True, follow_redirects=True,
) )
assert resp.status_code == 200 assert resp.status_code == 200
assert "The bill has been added" in resp.data.decode() assert "The bill has been added" in resp.data.decode()
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={"If-Modified-Since": before_str}, headers={"If-Modified-Since": "Tue, 27 Jul 2023 12:00:00 UTC"},
) )
assert resp.headers.get("Last-Modified") == "Thu, 27 Jul 2023 13:00:00 UTC"
assert resp.status_code == 200 assert resp.status_code == 200
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={"If-Modified-Since": after_str}, headers={"If-Modified-Since": "Tue, 27 Jul 2023 14:00:00 UTC"},
) )
assert resp.status_code == 304 assert resp.status_code == 304
def test_rss_etag_headers(self): def test_rss_etag_headers(self):
# Project creation # Project creation
self.post_project("raclette") with fake_time("2023-07-26 13:00:00"):
self.client.post("/raclette/members/add", data={"name": "george"}) self.post_project("raclette")
project = self.get_project("raclette") self.client.post("/raclette/members/add", data={"name": "george"})
token = project.generate_token("feed") project = self.get_project("raclette")
token = project.generate_token("feed")
resp = self.client.get(f"/raclette/feed/{token}.xml") resp = self.client.get(f"/raclette/feed/{token}.xml")
etag = resp.headers.get("ETag") assert resp.headers.get("ETag") == build_etag(
assert resp.status_code == 200 project.id, "2023-07-26T13:00:00"
)
assert resp.status_code == 200
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={ headers={
"If-None-Match": etag, "If-None-Match": build_etag(project.id, "2023-07-26T12:00:00"),
}, },
) )
assert resp.status_code == 304 assert resp.status_code == 200
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
@ -2081,38 +1952,39 @@ class TestBudget(IhatemoneyTestCase):
"If-None-Match": build_etag(project.id, "2023-07-26T13:00:00"), "If-None-Match": build_etag(project.id, "2023-07-26T13:00:00"),
}, },
) )
assert resp.status_code == 200 assert resp.status_code == 304
# Add bill # Add bill
self.login("raclette") with fake_time("2023-07-27 13:00:00"):
resp = self.client.post( self.login("raclette")
"/raclette/add", resp = self.client.post(
data={ "/raclette/add",
"date": "2016-12-31", data={
"what": "fromage à raclette", "date": "2016-12-31",
"payer": 1, "what": "fromage à raclette",
"payed_for": [1], "payer": 1,
"amount": "12", "payed_for": [1],
"bill_type": "Expense", "amount": "12",
"original_currency": "XXX", "original_currency": "XXX",
}, },
follow_redirects=True, follow_redirects=True,
) )
assert resp.status_code == 200 assert resp.status_code == 200
assert "The bill has been added" in resp.data.decode() assert "The bill has been added" in resp.data.decode()
etag = resp.headers.get("ETag")
resp = self.client.get(
f"/raclette/feed/{token}.xml",
headers={"If-None-Match": etag},
)
assert resp.status_code == 200
new_etag = resp.headers.get("ETag")
resp = self.client.get( resp = self.client.get(
f"/raclette/feed/{token}.xml", f"/raclette/feed/{token}.xml",
headers={ headers={
"If-None-Match": new_etag, "If-None-Match": build_etag(project.id, "2023-07-27T12:00:00"),
},
)
assert resp.headers.get("ETag") == build_etag(project.id, "2023-07-27T13:00:00")
assert resp.status_code == 200
resp = self.client.get(
f"/raclette/feed/{token}.xml",
headers={
"If-None-Match": build_etag(project.id, "2023-07-27T13:00:00"),
}, },
) )
assert resp.status_code == 304 assert resp.status_code == 304
@ -2200,7 +2072,6 @@ class TestBudget(IhatemoneyTestCase):
"payer": members_ids[1], "payer": members_ids[1],
"payed_for": members_ids, "payed_for": members_ids,
"amount": "25", "amount": "25",
"bill_type": "Expense",
}, },
) )
@ -2218,7 +2089,6 @@ class TestBudget(IhatemoneyTestCase):
"payer": members_ids_tartif[2], "payer": members_ids_tartif[2],
"payed_for": members_ids_tartif, "payed_for": members_ids_tartif,
"amount": "24", "amount": "24",
"bill_type": "Expense",
}, },
) )
@ -2253,7 +2123,6 @@ class TestBudget(IhatemoneyTestCase):
"payer": members_ids[1], "payer": members_ids[1],
"payed_for": members_ids[1:], "payed_for": members_ids[1:],
"amount": "25", "amount": "25",
"bill_type": "Expense",
}, },
) )

View file

@ -1,32 +1,17 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
from flask import Flask from flask import Flask
from jinja2 import FileSystemBytecodeCache
import pytest import pytest
from ihatemoney.babel_utils import compile_catalogs
from ihatemoney.currency_convertor import CurrencyConverter from ihatemoney.currency_convertor import CurrencyConverter
from ihatemoney.run import create_app, db from ihatemoney.run import create_app, db
@pytest.fixture(autouse=True, scope="session")
def babel_catalogs():
compile_catalogs()
@pytest.fixture(scope="session")
def jinja_cache_directory(tmp_path_factory):
return tmp_path_factory.mktemp("cache")
@pytest.fixture @pytest.fixture
def app(request: pytest.FixtureRequest, jinja_cache_directory): def app(request: pytest.FixtureRequest):
"""Create the Flask app with database""" """Create the Flask app with database"""
app = create_app(request.cls) app = create_app(request.cls)
# Caches the jinja templates so they are compiled only once per test session
app.jinja_env.bytecode_cache = FileSystemBytecodeCache(jinja_cache_directory)
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
request.cls.app = app request.cls.app = app

View file

@ -212,7 +212,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": user_id, "payer": user_id,
"payed_for": [user_id], "payed_for": [user_id],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
follow_redirects=True, follow_redirects=True,
@ -229,7 +228,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": user_id, "payer": user_id,
"payed_for": [user_id], "payed_for": [user_id],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
follow_redirects=True, follow_redirects=True,
@ -373,7 +371,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
follow_redirects=True, follow_redirects=True,
@ -394,7 +391,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "new thing", "what": "new thing",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
follow_redirects=True, follow_redirects=True,
@ -481,7 +477,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "Bill 1", "what": "Bill 1",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
) )
@ -492,7 +487,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "Bill 2", "what": "Bill 2",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -511,7 +505,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "Bill 1", "what": "Bill 1",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "88", "amount": "88",
}, },
) )
@ -552,7 +545,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "Bill 1", "what": "Bill 1",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "25", "amount": "25",
}, },
) )
@ -575,7 +567,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "Bill 2", "what": "Bill 2",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -636,7 +627,6 @@ class TestHistory(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "10", "amount": "10",
"original_currency": "EUR", "original_currency": "EUR",
}, },

View file

@ -3,7 +3,6 @@
DEBUG = False DEBUG = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///budget.db' SQLALCHEMY_DATABASE_URI = 'sqlite:///budget.db'
SQLACHEMY_ECHO = DEBUG SQLACHEMY_ECHO = DEBUG
SITE_NAME = "I Hate Money"
SECRET_KEY = "supersecret" SECRET_KEY = "supersecret"

View file

@ -16,13 +16,11 @@ def import_data(request: pytest.FixtureRequest):
"amount": 13.33, "amount": 13.33,
"payer_name": "tata", "payer_name": "tata",
"payer_weight": 1.0, "payer_weight": 1.0,
"bill_type": "Expense",
"owers": ["jeanne"], "owers": ["jeanne"],
}, },
{ {
"date": "2016-12-31", "date": "2016-12-31",
"what": "red wine", "what": "red wine",
"bill_type": "Expense",
"amount": 200.0, "amount": 200.0,
"payer_name": "jeanne", "payer_name": "jeanne",
"payer_weight": 1.0, "payer_weight": 1.0,
@ -30,7 +28,6 @@ def import_data(request: pytest.FixtureRequest):
}, },
{ {
"date": "2016-12-31", "date": "2016-12-31",
"bill_type": "Expense",
"what": "fromage a raclette", "what": "fromage a raclette",
"amount": 10.0, "amount": 10.0,
"payer_name": "zorglub", "payer_name": "zorglub",
@ -51,7 +48,6 @@ class CommonTestCase(object):
{ {
"date": "2017-01-01", "date": "2017-01-01",
"what": "refund", "what": "refund",
"bill_type": "Expense",
"amount": 13.33, "amount": 13.33,
"payer_name": "tata", "payer_name": "tata",
"payer_weight": 1.0, "payer_weight": 1.0,
@ -60,7 +56,6 @@ class CommonTestCase(object):
{ {
"date": "2016-12-31", "date": "2016-12-31",
"what": "red wine", "what": "red wine",
"bill_type": "Expense",
"amount": 200.0, "amount": 200.0,
"payer_name": "jeanne", "payer_name": "jeanne",
"payer_weight": 1.0, "payer_weight": 1.0,
@ -68,8 +63,7 @@ class CommonTestCase(object):
}, },
{ {
"date": "2016-12-31", "date": "2016-12-31",
"what": "a raclette", "what": "fromage a raclette",
"bill_type": "Expense",
"amount": 10.0, "amount": 10.0,
"payer_name": "zorglub", "payer_name": "zorglub",
"payer_weight": 2.0, "payer_weight": 2.0,
@ -114,7 +108,6 @@ class CommonTestCase(object):
assert b["currency"] == d["currency"] assert b["currency"] == d["currency"]
assert b["payer_weight"] == d["payer_weight"] assert b["payer_weight"] == d["payer_weight"]
assert b["date"] == d["date"] assert b["date"] == d["date"]
assert b["bill_type"] == d["bill_type"]
list_project = [ower for ower in b["owers"]] list_project = [ower for ower in b["owers"]]
list_project.sort() list_project.sort()
list_json = [ower for ower in d["owers"]] list_json = [ower for ower in d["owers"]]
@ -157,7 +150,6 @@ class CommonTestCase(object):
assert b["currency"] == "XXX" assert b["currency"] == "XXX"
assert b["payer_weight"] == d["payer_weight"] assert b["payer_weight"] == d["payer_weight"]
assert b["date"] == d["date"] assert b["date"] == d["date"]
assert b["bill_type"] == d["bill_type"]
list_project = [ower for ower in b["owers"]] list_project = [ower for ower in b["owers"]]
list_project.sort() list_project.sort()
list_json = [ower for ower in d["owers"]] list_json = [ower for ower in d["owers"]]
@ -216,7 +208,6 @@ class CommonTestCase(object):
assert b["currency"] == "EUR" assert b["currency"] == "EUR"
assert b["payer_weight"] == d["payer_weight"] assert b["payer_weight"] == d["payer_weight"]
assert b["date"] == d["date"] assert b["date"] == d["date"]
assert b["bill_type"] == d["bill_type"]
list_project = [ower for ower in b["owers"]] list_project = [ower for ower in b["owers"]]
list_project.sort() list_project.sort()
list_json = [ower for ower in d["owers"]] list_json = [ower for ower in d["owers"]]
@ -256,7 +247,6 @@ class CommonTestCase(object):
assert b["currency"] == "XXX" assert b["currency"] == "XXX"
assert b["payer_weight"] == d["payer_weight"] assert b["payer_weight"] == d["payer_weight"]
assert b["date"] == d["date"] assert b["date"] == d["date"]
assert b["bill_type"] == d["bill_type"]
list_project = [ower for ower in b["owers"]] list_project = [ower for ower in b["owers"]]
list_project.sort() list_project.sort()
list_json = [ower for ower in d["owers"]] list_json = [ower for ower in d["owers"]]
@ -281,7 +271,6 @@ class CommonTestCase(object):
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "red wine", "what": "red wine",
"bill_type": "Expense",
"payer": 2, "payer": 2,
"payed_for": [1, 3], "payed_for": [1, 3],
"amount": "200", "amount": "200",
@ -314,7 +303,6 @@ class CommonTestCase(object):
assert b["currency"] == d["currency"] assert b["currency"] == d["currency"]
assert b["payer_weight"] == d["payer_weight"] assert b["payer_weight"] == d["payer_weight"]
assert b["date"] == d["date"] assert b["date"] == d["date"]
assert b["bill_type"] == d["bill_type"]
list_project = [ower for ower in b["owers"]] list_project = [ower for ower in b["owers"]]
list_project.sort() list_project.sort()
list_json = [ower for ower in d["owers"]] list_json = [ower for ower in d["owers"]]
@ -338,7 +326,6 @@ class CommonTestCase(object):
{ {
"date": "2017-01-01", "date": "2017-01-01",
"what": "refund", "what": "refund",
"bill_type": "Reimbursement",
"payer_name": "tata", "payer_name": "tata",
"payer_weight": 1.0, "payer_weight": 1.0,
"owers": ["jeanne"], "owers": ["jeanne"],
@ -366,8 +353,7 @@ class TestExport(IhatemoneyTestCase):
"/raclette/add", "/raclette/add",
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"bill_type": "Expense", "what": "fromage à raclette",
"what": "à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3, 4], "payed_for": [1, 2, 3, 4],
"amount": "10.0", "amount": "10.0",
@ -378,7 +364,6 @@ class TestExport(IhatemoneyTestCase):
"/raclette/add", "/raclette/add",
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"bill_type": "Expense",
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1, 3], "payed_for": [1, 3],
@ -390,7 +375,6 @@ class TestExport(IhatemoneyTestCase):
"/raclette/add", "/raclette/add",
data={ data={
"date": "2017-01-01", "date": "2017-01-01",
"bill_type": "Reimbursement",
"what": "refund", "what": "refund",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
@ -403,7 +387,6 @@ class TestExport(IhatemoneyTestCase):
expected = [ expected = [
{ {
"date": "2017-01-01", "date": "2017-01-01",
"bill_type": "Reimbursement",
"what": "refund", "what": "refund",
"amount": 13.33, "amount": 13.33,
"currency": "XXX", "currency": "XXX",
@ -413,7 +396,6 @@ class TestExport(IhatemoneyTestCase):
}, },
{ {
"date": "2016-12-31", "date": "2016-12-31",
"bill_type": "Expense",
"what": "red wine", "what": "red wine",
"amount": 200.0, "amount": 200.0,
"currency": "XXX", "currency": "XXX",
@ -423,8 +405,7 @@ class TestExport(IhatemoneyTestCase):
}, },
{ {
"date": "2016-12-31", "date": "2016-12-31",
"bill_type": "Expense", "what": "fromage \xe0 raclette",
"what": "\xe0 raclette",
"amount": 10.0, "amount": 10.0,
"currency": "XXX", "currency": "XXX",
"payer_name": "zorglub", "payer_name": "zorglub",
@ -437,10 +418,10 @@ class TestExport(IhatemoneyTestCase):
# generate csv export of bills # generate csv export of bills
resp = self.client.get("/raclette/export/bills.csv") resp = self.client.get("/raclette/export/bills.csv")
expected = [ expected = [
"date,what,bill_type,amount,currency,payer_name,payer_weight,owers", "date,what,amount,currency,payer_name,payer_weight,owers",
"2017-01-01,refund,Reimbursement,XXX,13.33,tata,1.0,jeanne", "2017-01-01,refund,XXX,13.33,tata,1.0,jeanne",
'2016-12-31,red wine,Expense,XXX,200.0,jeanne,1.0,"zorglub, tata"', '2016-12-31,red wine,XXX,200.0,jeanne,1.0,"zorglub, tata"',
'2016-12-31,à raclette,Expense,10.0,XXX,zorglub,2.0,"zorglub, jeanne, tata, pépé"', '2016-12-31,fromage à raclette,10.0,XXX,zorglub,2.0,"zorglub, jeanne, tata, pépé"',
] ]
received_lines = resp.data.decode("utf-8").split("\n") received_lines = resp.data.decode("utf-8").split("\n")
@ -500,8 +481,7 @@ class TestExport(IhatemoneyTestCase):
"/raclette/add", "/raclette/add",
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "à raclette", "what": "fromage à raclette",
"bill_type": "Expense",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3, 4], "payed_for": [1, 2, 3, 4],
"amount": "10.0", "amount": "10.0",
@ -514,7 +494,6 @@ class TestExport(IhatemoneyTestCase):
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "poutine from Québec", "what": "poutine from Québec",
"bill_type": "Expense",
"payer": 2, "payer": 2,
"payed_for": [1, 3], "payed_for": [1, 3],
"amount": "100", "amount": "100",
@ -527,7 +506,6 @@ class TestExport(IhatemoneyTestCase):
data={ data={
"date": "2017-01-01", "date": "2017-01-01",
"what": "refund", "what": "refund",
"bill_type": "Reimbursement",
"payer": 3, "payer": 3,
"payed_for": [2], "payed_for": [2],
"amount": "13.33", "amount": "13.33",
@ -541,7 +519,6 @@ class TestExport(IhatemoneyTestCase):
{ {
"date": "2017-01-01", "date": "2017-01-01",
"what": "refund", "what": "refund",
"bill_type": "Reimbursement",
"amount": 13.33, "amount": 13.33,
"currency": "EUR", "currency": "EUR",
"payer_name": "tata", "payer_name": "tata",
@ -551,7 +528,6 @@ class TestExport(IhatemoneyTestCase):
{ {
"date": "2016-12-31", "date": "2016-12-31",
"what": "poutine from Qu\xe9bec", "what": "poutine from Qu\xe9bec",
"bill_type": "Expense",
"amount": 100.0, "amount": 100.0,
"currency": "CAD", "currency": "CAD",
"payer_name": "jeanne", "payer_name": "jeanne",
@ -561,7 +537,6 @@ class TestExport(IhatemoneyTestCase):
{ {
"date": "2016-12-31", "date": "2016-12-31",
"what": "fromage \xe0 raclette", "what": "fromage \xe0 raclette",
"bill_type": "Expense",
"amount": 10.0, "amount": 10.0,
"currency": "EUR", "currency": "EUR",
"payer_name": "zorglub", "payer_name": "zorglub",
@ -574,10 +549,10 @@ class TestExport(IhatemoneyTestCase):
# generate csv export of bills # generate csv export of bills
resp = self.client.get("/raclette/export/bills.csv") resp = self.client.get("/raclette/export/bills.csv")
expected = [ expected = [
"date,what,bill_type,amount,currency,payer_name,payer_weight,owers", "date,what,amount,currency,payer_name,payer_weight,owers",
"2017-01-01,refund,Reimbursement,13.33,EUR,tata,1.0,jeanne", "2017-01-01,refund,13.33,EUR,tata,1.0,jeanne",
'2016-12-31,poutine from Québec,Expense,100.0,CAD,jeanne,1.0,"zorglub, tata"', '2016-12-31,poutine from Québec,100.0,CAD,jeanne,1.0,"zorglub, tata"',
'2016-12-31,à raclette,Expense,10.0,EUR,zorglub,2.0,"zorglub, jeanne, tata, pépé"', '2016-12-31,fromage à raclette,10.0,EUR,zorglub,2.0,"zorglub, jeanne, tata, pépé"',
] ]
received_lines = resp.data.decode("utf-8").split("\n") received_lines = resp.data.decode("utf-8").split("\n")
@ -668,7 +643,6 @@ class TestExport(IhatemoneyTestCase):
data={ data={
"date": "2016-12-31", "date": "2016-12-31",
"what": "=COS(36)", "what": "=COS(36)",
"bill_type": "Expense",
"payer": 1, "payer": 1,
"payed_for": [1], "payed_for": [1],
"amount": "10.0", "amount": "10.0",
@ -679,8 +653,8 @@ class TestExport(IhatemoneyTestCase):
# generate csv export of bills # generate csv export of bills
resp = self.client.get("/raclette/export/bills.csv") resp = self.client.get("/raclette/export/bills.csv")
expected = [ expected = [
"date,what,bill_type,amount,currency,payer_name,payer_weight,owers", "date,what,amount,currency,payer_name,payer_weight,owers",
"2016-12-31,'=COS(36),Expense,10.0,EUR,zorglub,1.0,zorglub", "2016-12-31,'=COS(36),10.0,EUR,zorglub,1.0,zorglub",
] ]
received_lines = resp.data.decode("utf-8").split("\n") received_lines = resp.data.decode("utf-8").split("\n")

View file

@ -3,17 +3,13 @@ import smtplib
import socket import socket
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import pytest
from sqlalchemy import orm from sqlalchemy import orm
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
from ihatemoney import models from ihatemoney import models
from ihatemoney.currency_convertor import CurrencyConverter from ihatemoney.currency_convertor import CurrencyConverter
from ihatemoney.manage import ( from ihatemoney.manage import delete_project, generate_config, password_hash
delete_project,
generate_config,
get_project_count,
password_hash,
)
from ihatemoney.run import load_configuration from ihatemoney.run import load_configuration
from ihatemoney.tests.common.ihatemoney_testcase import BaseTestCase, IhatemoneyTestCase from ihatemoney.tests.common.ihatemoney_testcase import BaseTestCase, IhatemoneyTestCase
@ -130,7 +126,6 @@ class TestModels(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -142,7 +137,6 @@ class TestModels(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -154,7 +148,6 @@ class TestModels(IhatemoneyTestCase):
"what": "delicatessen", "what": "delicatessen",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -188,7 +181,6 @@ class TestModels(IhatemoneyTestCase):
"what": "fromage à raclette", "what": "fromage à raclette",
"payer": 1, "payer": 1,
"payed_for": [1, 2, 3], "payed_for": [1, 2, 3],
"bill_type": "Expense",
"amount": "10.0", "amount": "10.0",
}, },
) )
@ -200,7 +192,6 @@ class TestModels(IhatemoneyTestCase):
"what": "red wine", "what": "red wine",
"payer": 2, "payer": 2,
"payed_for": [1], "payed_for": [1],
"bill_type": "Expense",
"amount": "20", "amount": "20",
}, },
) )
@ -212,7 +203,6 @@ class TestModels(IhatemoneyTestCase):
"what": "delicatessen", "what": "delicatessen",
"payer": 1, "payer": 1,
"payed_for": [1, 2], "payed_for": [1, 2],
"bill_type": "Expense",
"amount": "10", "amount": "10",
}, },
) )
@ -233,65 +223,6 @@ class TestModels(IhatemoneyTestCase):
pay_each_expected = 10 / 3 pay_each_expected = 10 / 3
assert bill.pay_each() == pay_each_expected assert bill.pay_each() == pay_each_expected
def test_demo_project_count(self):
"""Test command the get-project-count"""
self.post_project("raclette")
# add members
self.client.post("/raclette/members/add", data={"name": "zorglub", "weight": 2})
self.client.post("/raclette/members/add", data={"name": "fred"})
self.client.post("/raclette/members/add", data={"name": "tata"})
self.client.post("/raclette/members/add", data={"name": "pépé"})
# create bills
self.client.post(
"/raclette/add",
data={
"date": "2011-08-10",
"what": "fromage à raclette",
"payer": 1,
"payed_for": [1, 2, 3],
"amount": "10.0",
},
)
self.client.post(
"/raclette/add",
data={
"date": "2011-08-10",
"what": "red wine",
"payer": 2,
"payed_for": [1],
"amount": "20",
},
)
assert self.get_project("raclette").has_bills()
# Now check the different parameters
runner = self.app.test_cli_runner()
result0 = runner.invoke(get_project_count)
assert result0.output.strip() == "Number of projects: 1"
# With more than 1 bill, without printing emails
result1 = runner.invoke(get_project_count, "False 1")
assert result1.output.strip() == "Number of projects: 1"
# With more than 2 bill, without printing emails
result2 = runner.invoke(get_project_count, "False 2")
assert result2.output.strip() == "Number of projects: 0"
# With more than 0 days old
result3 = runner.invoke(get_project_count, "False 0 0")
assert result3.output.strip() == "Number of projects: 0"
result4 = runner.invoke(get_project_count, "False 0 20000")
assert result4.output.strip() == "Number of projects: 1"
# Print emails
result5 = runner.invoke(get_project_count, "True")
assert "raclette@notmyidea.org" in result5.output
class TestEmailFailure(IhatemoneyTestCase): class TestEmailFailure(IhatemoneyTestCase):
def test_creation_email_failure_smtp(self): def test_creation_email_failure_smtp(self):
@ -464,7 +395,9 @@ class TestCurrencyConverter:
def test_failing_remote(self): def test_failing_remote(self):
rates = {} rates = {}
with patch("requests.Response.json", new=lambda _: {}): with patch("requests.Response.json", new=lambda _: {}), pytest.warns(
UserWarning
):
# we need a non-patched converter, but it seems that MagickMock # we need a non-patched converter, but it seems that MagickMock
# is mocking EVERY instance of the class method. Too bad. # is mocking EVERY instance of the class method. Too bad.
rates = CurrencyConverter.get_rates(self.converter) rates = CurrencyConverter.get_rates(self.converter)

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -782,7 +782,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -785,7 +785,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-07-03 19:09+0000\n" "PO-Revision-Date: 2022-09-12 15:25+0000\n"
"Last-Translator: Quentin PAGÈS <quentinantonin@free.fr>\n" "Last-Translator: Maite Guix <maite.guix@gmail.com>\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/ca/>\n"
"Language: ca\n" "Language: ca\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/i-hate-"
"money/i-hate-money/ca/>\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -222,7 +222,7 @@ msgid "{start_object}, {next_object}"
msgstr "{start_object}, {next_object}" msgstr "{start_object}, {next_object}"
msgid "No Currency" msgid "No Currency"
msgstr "Cap moneda" msgstr "Sense moneda"
#. Form error with only one error #. Form error with only one error
msgid "{prefix}: {error}" msgid "{prefix}: {error}"
@ -826,7 +826,7 @@ msgstr "Aplicació mòbil"
msgid "Documentation" msgid "Documentation"
msgstr "Documentació" msgstr "Documentació"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Panell d'administració" msgstr "Panell d'administració"
msgid "Legal information" msgid "Legal information"
@ -1082,3 +1082,4 @@ msgstr "Període"
#~ msgstr "" #~ msgstr ""
#~ "Pots compartir directament l'enllaç següent" #~ "Pots compartir directament l'enllaç següent"
#~ " a través del teu mitjà preferit" #~ " a través del teu mitjà preferit"

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -3,8 +3,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-02-24 00:03+0000\n" "PO-Revision-Date: 2023-08-20 00:58+0000\n"
"Last-Translator: Peter <peteramried@web.de>\n" "Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
"Language-Team: German <https://hosted.weblate.org/projects/i-hate-money/" "Language-Team: German <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/de/>\n" "i-hate-money/de/>\n"
"Language: de\n" "Language: de\n"
@ -12,7 +12,7 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.5-dev\n" "X-Generator: Weblate 5.0-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -35,7 +35,7 @@ msgid "Current private code"
msgstr "Aktueller privater Code" msgstr "Aktueller privater Code"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "Geben Sie Ihren privaten Code ein, um das Projekt zu bearbeiten" msgstr ""
msgid "New private code" msgid "New private code"
msgstr "Neuer privater Code" msgstr "Neuer privater Code"
@ -197,15 +197,16 @@ msgid "Logout"
msgstr "Ausloggen" msgstr "Ausloggen"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "Bitte überprüfen Sie die E-Mail Konfiguration des Servers." msgstr ""
#, python-format #, fuzzy, python-format
msgid "" msgid ""
"Please check the email configuration of the server or contact the " "Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s" "administrator: %(admin_email)s"
msgstr "" msgstr ""
"Bitte überprüfe die E-Mail Konfiguration des Servers oder kontaktiere einen " "Entschuldigung, es trat ein Fehler beim Versenden der Einladungsmails "
"Administrator: %(admin_email)s" "auf. Bitte überprüfe die E-Mail Konfiguration des Servers oder "
"kontaktiere einen Administrator."
#. List with two items only #. List with two items only
msgid "{dual_object_0} and {dual_object_1}" msgid "{dual_object_0} and {dual_object_1}"
@ -234,8 +235,11 @@ msgstr "{prefix}: {error}"
msgid "{prefix}:<br />{errors}" msgid "{prefix}:<br />{errors}"
msgstr "{prefix}:<br />{errors}" msgstr "{prefix}:<br />{errors}"
#, fuzzy
msgid "Too many failed login attempts." msgid "Too many failed login attempts."
msgstr "Zu viele fehlgeschlagene Anmeldeversuche." msgstr ""
"Zu viele fehlgeschlagene Anmeldeversuche, bitte versuche es später "
"nochmal."
#, python-format #, python-format
msgid "This admin password is not the right one. Only %(num)d attempts left." msgid "This admin password is not the right one. Only %(num)d attempts left."
@ -280,16 +284,15 @@ msgstr "Unbekanntes Projekt"
msgid "Password successfully reset." msgid "Password successfully reset."
msgstr "Passwort erfolgreich zurückgesetzt." msgstr "Passwort erfolgreich zurückgesetzt."
#, fuzzy
msgid "Project settings have been changed successfully." msgid "Project settings have been changed successfully."
msgstr "Einstellungen wurden erfolgreich übernommen." msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "" msgstr ""
#, fuzzy, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
msgstr "Fehlendes Attribut: %(attribute)s" msgstr ""
msgid "" msgid ""
"Cannot add bills in multiple currencies to a project without default " "Cannot add bills in multiple currencies to a project without default "
@ -308,7 +311,7 @@ msgid "Error deleting project"
msgstr "Fehler bei Projektlöschung" msgstr "Fehler bei Projektlöschung"
msgid "Unable to logout" msgid "Unable to logout"
msgstr "Verlassen nicht möglich" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -317,9 +320,12 @@ msgstr "Du wurdest eingeladen, deine Ausgaben für %(project)s zu teilen"
msgid "Your invitations have been sent" msgid "Your invitations have been sent"
msgstr "Deine Einladungen wurden versendet" msgstr "Deine Einladungen wurden versendet"
#, fuzzy
msgid "Sorry, there was an error while trying to send the invitation emails." msgid "Sorry, there was an error while trying to send the invitation emails."
msgstr "" msgstr ""
"Entschuldigung, es trat ein Fehler beim Versenden der Einladungsmails auf." "Entschuldigung, es trat ein Fehler beim Versenden der Einladungsmails "
"auf. Bitte überprüfe die E-Mail Konfiguration des Servers oder "
"kontaktiere einen Administrator."
#, python-format #, python-format
msgid "%(member)s has been added" msgid "%(member)s has been added"
@ -365,7 +371,7 @@ msgstr "Die Ausgabe wurde bearbeitet"
#, python-format #, python-format
msgid "%(lang)s is not a supported language" msgid "%(lang)s is not a supported language"
msgstr "%(lang)s ist keine unterstützte Sprache" msgstr ""
msgid "Error deleting project history" msgid "Error deleting project history"
msgstr "Projekthistorie konnte nicht gelöscht werden" msgstr "Projekthistorie konnte nicht gelöscht werden"
@ -480,7 +486,7 @@ msgstr "Dies wird alle Ausgaben und Mitglieder dieses Projektes löschen!"
#, fuzzy #, fuzzy
msgid "Import previously exported project" msgid "Import previously exported project"
msgstr "Zuvor exportierte Projekt-Datei importieren" msgstr "Zuvor exportierte JSON-Datei importieren"
msgid "Choose file" msgid "Choose file"
msgstr "Datei auswählen" msgstr "Datei auswählen"
@ -491,9 +497,8 @@ msgstr "Ausgabe bearbeiten"
msgid "Add a bill" msgid "Add a bill"
msgstr "Ausgabe hinzufügen" msgstr "Ausgabe hinzufügen"
#, fuzzy
msgid "Simple operations are allowed, e.g. (18+36.2)/3" msgid "Simple operations are allowed, e.g. (18+36.2)/3"
msgstr "einfache Operationen sind erlaubt, z.B. (18+36.2)/3" msgstr ""
msgid "Everyone" msgid "Everyone"
msgstr "Jeder" msgstr "Jeder"
@ -585,14 +590,12 @@ msgstr "Rechnung %(name)s: %(owers_list_str)s zur Eigentümerliste hinzugefügt"
msgid "Bill %(name)s: removed %(owers_list_str)s from owers list" msgid "Bill %(name)s: removed %(owers_list_str)s from owers list"
msgstr "Rechnung %(name)s: %(owers_list_str)s von der Eigentümerliste entfernt" msgstr "Rechnung %(name)s: %(owers_list_str)s von der Eigentümerliste entfernt"
#, fuzzy
msgid "This project has history disabled. New actions won't appear below." msgid "This project has history disabled. New actions won't appear below."
msgstr "" msgstr ""
"Dieses Projekt hat den Projektverlauf deaktiviert. Neue Aktionen werden "
"unten nicht mehr angezeigt."
#, fuzzy
msgid "You can enable history on the settings page." msgid "You can enable history on the settings page."
msgstr "Der Verlauf kann in den Einstellungen aktiviert werden." msgstr "IP-Adresserfassung kann in den Einstellungen aktiviert werden"
msgid "" msgid ""
"The table below reflects actions recorded prior to disabling project " "The table below reflects actions recorded prior to disabling project "
@ -813,7 +816,7 @@ msgstr "Dashboard"
#, python-format #, python-format
msgid "Please retry after %(date)s." msgid "Please retry after %(date)s."
msgstr "Bitte nach %(date)s erneut versuchen." msgstr ""
msgid "Code" msgid "Code"
msgstr "Code" msgstr "Code"
@ -824,7 +827,7 @@ msgstr "Handy-Applikation"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentation" msgstr "Dokumentation"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Dashboard Administration" msgstr "Dashboard Administration"
msgid "Legal information" msgid "Legal information"
@ -878,8 +881,9 @@ msgstr "Keine Ausgaben"
msgid "Nothing to list yet." msgid "Nothing to list yet."
msgstr "Noch nichts aufzulisten." msgstr "Noch nichts aufzulisten."
#, fuzzy
msgid "Add your first bill" msgid "Add your first bill"
msgstr "Mach deine erste Rechnung" msgstr "eine Ausgabe hinzufügen"
#, fuzzy #, fuzzy
msgid "Add the first participant" msgid "Add the first participant"
@ -908,7 +912,7 @@ msgid "Invite people to join this project"
msgstr "Lade Leute ein, diesem Projekt beizutreten" msgstr "Lade Leute ein, diesem Projekt beizutreten"
msgid "Share an invitation link" msgid "Share an invitation link"
msgstr "Einladung verschicken" msgstr ""
msgid "" msgid ""
"The easiest way to invite people is to give them the following invitation" "The easiest way to invite people is to give them the following invitation"
@ -916,29 +920,26 @@ msgid ""
" add/edit/delete bills. However, they will not have access to important " " add/edit/delete bills. However, they will not have access to important "
"settings such as changing the private code or deleting the whole project." "settings such as changing the private code or deleting the whole project."
msgstr "" msgstr ""
"Am einfachsten lädt man jemanden ein, indem man folgenden Einladungs-Link "
"teilt. <br /> Sie werden dann in der Lage sein dem Projekt beizutreten, zu "
"bearbeiten und Rechnungen hinzuzufügen, bearbeiten und löschen. Sie werden "
"allerdings keinen Zugriff auf wichtige Einstellungen bekommen, wie Privaten "
"Code zu ändern oder gar das ganze Projekt zu löschen."
msgid "Scan QR code" msgid "Scan QR code"
msgstr "QR-Code scannen" msgstr ""
msgid "Use a mobile device with a compatible app." msgid "Use a mobile device with a compatible app."
msgstr "Benutzen sie ein Smartphone mit Kompatibler App." msgstr ""
msgid "Send via Emails" msgid "Send via Emails"
msgstr "Per E-Mail versenden" msgstr "Per E-Mail versenden"
#, fuzzy
msgid "" msgid ""
"Specify a list of email adresses (separated by comma) of people you want " "Specify a list of email adresses (separated by comma) of people you want "
"to notify about the creation of this project. We will send them an email " "to notify about the creation of this project. We will send them an email "
"with the invitation link." "with the invitation link."
msgstr "" msgstr ""
"Gib eine (durch Kommas getrennte) Liste von E-Mail-Adressen an, die du über " "Gib eine (durch Kommas getrennte) Liste von E-Mail-Adressen an, die du "
"die Erstellung dieses Projekts informieren möchtest, und wir werden ihnen " "über die\n"
"eine E-Mail senden." "\t\t\tErstellung dieses Projekts informieren möchtest, und wir senden "
"ihnen eine E-Mail."
msgid "Share Identifier & code" msgid "Share Identifier & code"
msgstr "Teile die ID & den Code" msgstr "Teile die ID & den Code"
@ -953,11 +954,12 @@ msgstr ""
msgid "Identifier:" msgid "Identifier:"
msgstr "ID:" msgstr "ID:"
#, fuzzy
msgid "Private code:" msgid "Private code:"
msgstr "Privater Code:" msgstr "Privater Code"
msgid "the private code was defined when you created the project" msgid "the private code was defined when you created the project"
msgstr "Der Private Code wurde beim erstellen des Projekts von Ihnen festgelegt" msgstr ""
msgid "Who pays?" msgid "Who pays?"
msgstr "Wer zahlt?" msgstr "Wer zahlt?"

Binary file not shown.

View file

@ -811,7 +811,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
#, fuzzy #, fuzzy

Binary file not shown.

View file

@ -821,7 +821,7 @@ msgstr "Poŝaparata programo"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentaro" msgstr "Dokumentaro"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Administra panelo" msgstr "Administra panelo"
#, fuzzy #, fuzzy

Binary file not shown.

View file

@ -818,7 +818,7 @@ msgstr "Aplicación móvil"
msgid "Documentation" msgid "Documentation"
msgstr "Documentación" msgstr "Documentación"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Panel de administración" msgstr "Panel de administración"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -1,18 +1,19 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2023-11-27 05:02+0000\n" "PO-Revision-Date: 2022-04-11 17:12+0000\n"
"Last-Translator: Wilfredo Gomez <thepageguy@mailfence.com>\n" "Last-Translator: Santiago José Gutiérrez Llanos "
"Language-Team: Spanish (Latin America) <https://hosted.weblate.org/projects/" "<gutierrezapata17@gmail.com>\n"
"i-hate-money/i-hate-money/es_419/>\n"
"Language: es_419\n" "Language: es_419\n"
"Language-Team: Spanish (Latin America) "
"<https://hosted.weblate.org/projects/i-hate-money/i-hate-money/es_419/>\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.2.1-rc\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -29,17 +30,18 @@ msgstr ""
msgid "Project name" msgid "Project name"
msgstr "Nombre del Proyecto" msgstr "Nombre del Proyecto"
#, fuzzy
msgid "Current private code" msgid "Current private code"
msgstr "código privado actual" msgstr "Nuevo código privado"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "Ingrese el código privado existente para editar el proyecto" msgstr ""
msgid "New private code" msgid "New private code"
msgstr "Nuevo Código privado" msgstr "Nuevo código privado"
msgid "Enter a new code if you want to change it" msgid "Enter a new code if you want to change it"
msgstr "Introduce un nuevo código si quieres cambiarlo" msgstr "Entra un nuevo código si tu quieres cambiarlo"
msgid "Email" msgid "Email"
msgstr "Correo Electrónico" msgstr "Correo Electrónico"
@ -48,31 +50,31 @@ msgid "Enable project history"
msgstr "Habilitar historial del proyecto" msgstr "Habilitar historial del proyecto"
msgid "Use IP tracking for project history" msgid "Use IP tracking for project history"
msgstr "Utilice el seguimiento de IP para el historial del proyecto" msgstr "Registrar la IPs para el historial del proyecto"
msgid "Default Currency" msgid "Default Currency"
msgstr "Moneda por defecto" msgstr "Moneda por defecto"
msgid "Setting a default currency enables currency conversion between bills" msgid "Setting a default currency enables currency conversion between bills"
msgstr "" msgstr ""
"Establecer una moneda predeterminada permite la conversión de moneda entre " "Establecer una moneda predeterminada permite la conversión de divisas "
"billetes" "entre facturas"
msgid "Unknown error" msgid "Unknown error"
msgstr "Error desconocido" msgstr "Error desconocido"
msgid "Invalid private code." msgid "Invalid private code."
msgstr "Código privado no válido." msgstr "Código privado inválido."
msgid "" msgid ""
"This project cannot be set to 'no currency' because it contains bills in " "This project cannot be set to 'no currency' because it contains bills in "
"multiple currencies." "multiple currencies."
msgstr "" msgstr ""
"Este proyecto no se puede configurar como \"sin moneda\" porque contiene " "Este proyecto no se puede establecer en 'ninguna moneda' porque contiene "
"billetes en varias monedas." "facturas en varias monedas."
msgid "Compatible with Cospend" msgid "Compatible with Cospend"
msgstr "Compatible con Cospend" msgstr ""
msgid "Project identifier" msgid "Project identifier"
msgstr "Identificador de proyecto" msgstr "Identificador de proyecto"
@ -92,16 +94,16 @@ msgstr ""
"favor, elija un nuevo identificador" "favor, elija un nuevo identificador"
msgid "Which is a real currency: Euro or Petro dollar?" msgid "Which is a real currency: Euro or Petro dollar?"
msgstr "¿Cuál es la moneda real: el euro o el petrodólar?" msgstr "¿Cuál es una moneda real: euro o petro dólar?"
msgid "euro" msgid "euro"
msgstr "euro" msgstr "Euro"
msgid "Please, validate the captcha to proceed." msgid "Please, validate the captcha to proceed."
msgstr "Por favor, valide la captcha para proceder." msgstr "Por favor, completa el captcha para seguir."
msgid "Enter private code to confirm deletion" msgid "Enter private code to confirm deletion"
msgstr "Ingrese el código privado para confirmar la eliminación" msgstr "Introduzca el código privado para confirmar la eliminación"
msgid "Get in" msgid "Get in"
msgstr "Entrar" msgstr "Entrar"
@ -122,7 +124,7 @@ msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
msgid "Password confirmation" msgid "Password confirmation"
msgstr "confirmación de contraseña" msgstr "Confirmar contraseña"
msgid "Reset password" msgid "Reset password"
msgstr "Restablecer contraseña" msgstr "Restablecer contraseña"
@ -159,7 +161,7 @@ msgstr "Enviar y agregar uno nuevo"
#, python-format #, python-format
msgid "Project default: %(currency)s" msgid "Project default: %(currency)s"
msgstr "Projecto por defecto: %(currency)s" msgstr "moneda predeterminada del projecto: %(currency)s"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
@ -194,15 +196,15 @@ msgstr "Cerrar sesión"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "" msgstr ""
"Por favor verifique la configuración de correo electrónico del servidor."
#, python-format #, fuzzy, python-format
msgid "" msgid ""
"Please check the email configuration of the server or contact the " "Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s" "administrator: %(admin_email)s"
msgstr "" msgstr ""
"Verifique la configuración de correo electrónico del servidor o comuníquese " "Lo sentimos, hubo un error cuando intentamos enviarle correos de "
"con el administrador: %(admin_email)s" "invitación. Por favor, revise la configuración de correo en el servidor o"
" contactese con el administrador."
#. List with two items only #. List with two items only
msgid "{dual_object_0} and {dual_object_1}" msgid "{dual_object_0} and {dual_object_1}"
@ -221,47 +223,53 @@ msgid "{start_object}, {next_object}"
msgstr "{start_object}, {next_object}" msgstr "{start_object}, {next_object}"
msgid "No Currency" msgid "No Currency"
msgstr "No Moneda" msgstr "no moneda"
#. Form error with only one error #. Form error with only one error
msgid "{prefix}: {error}" msgid "{prefix}: {error}"
msgstr "{prefix}: {error}" msgstr "{prefijo}: {error}"
#. Form error with a list of errors #. Form error with a list of errors
msgid "{prefix}:<br />{errors}" msgid "{prefix}:<br />{errors}"
msgstr "{prefix}:<br />{errors}" msgstr "{prefijo}:<br />{errores}"
#, fuzzy
msgid "Too many failed login attempts." msgid "Too many failed login attempts."
msgstr "Demasiados intentos fallidos de inicio de sesión." msgstr ""
"Demasiados intentos fallidos de inicio de sesión, vuelva a intentarlo más"
" tarde."
#, python-format #, python-format
msgid "This admin password is not the right one. Only %(num)d attempts left." msgid "This admin password is not the right one. Only %(num)d attempts left."
msgstr "" msgstr ""
"Esta contraseña de administrador no es la correcta. Solo quedan %(num)d " "Esta contraseña de administrador no es la correcta. Solo quedan %(num)d "
"intentos restantes." "intentos."
msgid "Provided token is invalid" msgid "Provided token is invalid"
msgstr "El token proporcionado no es válido" msgstr "La muestra proporcionada no es válida"
msgid "This private code is not the right one" msgid "This private code is not the right one"
msgstr "Este código privado no es el correcto" msgstr "Este código privado no es el correcto"
msgid "A reminder email has just been sent to you" msgid "A reminder email has just been sent to you"
msgstr "Se le acaba de enviar un correo electrónico de recordatorio" msgstr "Acabamos de enviarte un email de recordatorio"
msgid "" msgid ""
"We tried to send you an reminder email, but there was an error. You can " "We tried to send you an reminder email, but there was an error. You can "
"still use the project normally." "still use the project normally."
msgstr "" msgstr ""
"Intentamos enviarte un correo electrónico de recordatorio, pero hubo un " "Te hemos intentado enviar un correo electrónico recordatorio pero ha "
"error. Aún puedes usar el proyecto normalmente." "habido un error. Todavía puedes usar el proyecto habitualmente."
#, fuzzy
msgid "" msgid ""
"Sorry, there was an error while sending you an email with password reset " "Sorry, there was an error while sending you an email with password reset "
"instructions." "instructions."
msgstr "" msgstr ""
"Lo sentimos, hubo un error al enviarle un correo electrónico con " "Lo sentimos, hubo un error al enviarle un correo electrónico con las "
"instrucciones para restablecer la contraseña." "instrucciones de restablecimiento de contraseña. Compruebe la "
"configuración de correo electrónico del servidor o póngase en contacto "
"con el administrador."
msgid "No token provided" msgid "No token provided"
msgstr "No se proporciono ningún token" msgstr "No se proporciono ningún token"
@ -276,33 +284,33 @@ msgid "Password successfully reset."
msgstr "Contraseña restablecida con éxito." msgstr "Contraseña restablecida con éxito."
msgid "Project settings have been changed successfully." msgid "Project settings have been changed successfully."
msgstr "La configuración del proyecto se ha cambiado correctamente." msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "No se puede analizar CSV" msgstr ""
#, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
msgstr "Atributo faltante: %(attribute)s" msgstr ""
msgid "" msgid ""
"Cannot add bills in multiple currencies to a project without default " "Cannot add bills in multiple currencies to a project without default "
"currency" "currency"
msgstr "" msgstr ""
"No se pueden agregar billetes en varias monedas a un proyecto sin moneda " "No se pueden agregar facturas en varias monedas a un proyecto sin la "
"predeterminada" "moneda predeterminada"
msgid "Project successfully uploaded" msgid "Project successfully uploaded"
msgstr "Proyecto cargado exitosamente" msgstr "El proyecto se subió exitosamente"
msgid "Project successfully deleted" msgid "Project successfully deleted"
msgstr "Proyecto eliminado correctamente" msgstr "Proyecto eliminado correctamente"
msgid "Error deleting project" msgid "Error deleting project"
msgstr "Error al borrar proyecto" msgstr "Error al borrar poryecto"
msgid "Unable to logout" msgid "Unable to logout"
msgstr "No se puede cerrar sesión" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -311,24 +319,26 @@ msgstr "Usted ha sido invitado a compartir sus gastos para %(project)s"
msgid "Your invitations have been sent" msgid "Your invitations have been sent"
msgstr "Sus invitaciones han sido enviadas" msgstr "Sus invitaciones han sido enviadas"
#, fuzzy
msgid "Sorry, there was an error while trying to send the invitation emails." msgid "Sorry, there was an error while trying to send the invitation emails."
msgstr "" msgstr ""
"Lo sentimos, hubo un error al intentar enviar los correos electrónicos de " "Lo sentimos, hubo un error cuando intentamos enviarle correos de "
"invitación." "invitación. Por favor, revise la configuración de correo en el servidor o"
" contactese con el administrador."
#, python-format #, python-format
msgid "%(member)s has been added" msgid "%(member)s has been added"
msgstr "%(member)s ha sido añadido" msgstr "Se añadieron %(member)s"
msgid "Error activating participant" msgid "Error activating participant"
msgstr "Error al activar el participante" msgstr "Error activando participante"
#, python-format #, python-format
msgid "%(name)s is part of this project again" msgid "%(name)s is part of this project again"
msgstr "%(name)s es parte de este proyecto otra vez" msgstr "%(name)s es parte de este nuevo proyecto"
msgid "Error removing participant" msgid "Error removing participant"
msgstr "Error al eliminar el participante" msgstr "Error eliminando participante"
#, python-format #, python-format
msgid "" msgid ""
@ -350,7 +360,7 @@ msgid "The bill has been added"
msgstr "La factura ha sido agregada" msgstr "La factura ha sido agregada"
msgid "Error deleting bill" msgid "Error deleting bill"
msgstr "Error al eliminar la factura" msgstr "Error eliminando factura"
msgid "The bill has been deleted" msgid "The bill has been deleted"
msgstr "La factura ha sido eliminada" msgstr "La factura ha sido eliminada"
@ -360,7 +370,7 @@ msgstr "La factura ha sido modificada"
#, python-format #, python-format
msgid "%(lang)s is not a supported language" msgid "%(lang)s is not a supported language"
msgstr "%(lang)s no es un idioma admitido" msgstr ""
msgid "Error deleting project history" msgid "Error deleting project history"
msgstr "Error al eliminar el historial del proyecto" msgstr "Error al eliminar el historial del proyecto"
@ -440,8 +450,9 @@ msgstr "Conseguir en"
msgid "Edit project" msgid "Edit project"
msgstr "Editar proyecto" msgstr "Editar proyecto"
#, fuzzy
msgid "Import project" msgid "Import project"
msgstr "Importar proyecto" msgstr "Editar proyecto"
msgid "Download project's data" msgid "Download project's data"
msgstr "Descargar datos del proyecto" msgstr "Descargar datos del proyecto"
@ -470,13 +481,14 @@ msgid "Privacy Settings"
msgstr "Ajustes de privacidad" msgstr "Ajustes de privacidad"
msgid "Save changes" msgid "Save changes"
msgstr "Guardar cambios" msgstr ""
msgid "This will remove all bills and participants in this project!" msgid "This will remove all bills and participants in this project!"
msgstr "Esto va a remover todas las facturas y participantes en este proyecto!" msgstr "Esto va a remover todas las facturas y participantes en este proyecto!"
#, fuzzy
msgid "Import previously exported project" msgid "Import previously exported project"
msgstr "Importar proyecto previamente exportado" msgstr "Importar archivo JSON previamente exportado"
msgid "Choose file" msgid "Choose file"
msgstr "Escoger un archivo" msgstr "Escoger un archivo"
@ -488,7 +500,7 @@ msgid "Add a bill"
msgstr "Agregar una factura" msgstr "Agregar una factura"
msgid "Simple operations are allowed, e.g. (18+36.2)/3" msgid "Simple operations are allowed, e.g. (18+36.2)/3"
msgstr "Se permiten operaciones simples, e.j. (18+36.2)/3" msgstr ""
msgid "Everyone" msgid "Everyone"
msgstr "Todos" msgstr "Todos"
@ -512,13 +524,13 @@ msgid "Download"
msgstr "Descargar" msgstr "Descargar"
msgid "Disabled Project History" msgid "Disabled Project History"
msgstr "Historial de proyectos deshabilitado" msgstr "Historial de proyecto activo"
msgid "Disabled Project History & IP Address Recording" msgid "Disabled Project History & IP Address Recording"
msgstr "Historial de proyecto y registros de dirección IP inactivos" msgstr "Historial de proyecto y registros de dirección IP inactivos"
msgid "Enabled Project History" msgid "Enabled Project History"
msgstr "Historial de proyectos habilitado" msgstr "Historial de proyecto activo"
msgid "Disabled IP Address Recording" msgid "Disabled IP Address Recording"
msgstr "Registro de direcciones IP activo" msgstr "Registro de direcciones IP activo"
@ -580,21 +592,19 @@ msgstr "Factura %(name)s: removida %(owers_list_str)s de la lista de dueños"
msgid "This project has history disabled. New actions won't appear below." msgid "This project has history disabled. New actions won't appear below."
msgstr "" msgstr ""
"Este proyecto tiene el historial deshabilitado. Las nuevas acciones no "
"aparecerán a continuación."
#, fuzzy
msgid "You can enable history on the settings page." msgid "You can enable history on the settings page."
msgstr "Puede habilitar el historial en la página de configuración." msgstr "El registro de direcciones IP se puede activar en la página de ajustes"
msgid "" msgid ""
"The table below reflects actions recorded prior to disabling project " "The table below reflects actions recorded prior to disabling project "
"history." "history."
msgstr "" msgstr ""
"La siguiente tabla refleja las acciones registradas antes de deshabilitar el "
"historial del proyecto."
#, fuzzy
msgid "You can clear the project history to remove them." msgid "You can clear the project history to remove them."
msgstr "Puede borrar el historial del proyecto para eliminarlos." msgstr "Es probable que alguien borrara el historial del proyecto."
msgid "" msgid ""
"Some entries below contain IP addresses, even though this project has IP " "Some entries below contain IP addresses, even though this project has IP "
@ -791,7 +801,7 @@ msgid "Settings"
msgstr "Configuración" msgstr "Configuración"
msgid "RSS Feed" msgid "RSS Feed"
msgstr "rss Feed" msgstr ""
msgid "Other projects :" msgid "Other projects :"
msgstr "Otros proyectos :" msgstr "Otros proyectos :"
@ -804,7 +814,7 @@ msgstr "Tablero"
#, python-format #, python-format
msgid "Please retry after %(date)s." msgid "Please retry after %(date)s."
msgstr "Vuelva a intentarlo después de %(date)s." msgstr ""
msgid "Code" msgid "Code"
msgstr "Código" msgstr "Código"
@ -815,7 +825,7 @@ msgstr "Aplicación móvil"
msgid "Documentation" msgid "Documentation"
msgstr "Documentación" msgstr "Documentación"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Panel de administración" msgstr "Panel de administración"
msgid "Legal information" msgid "Legal information"
@ -869,11 +879,13 @@ msgstr "Sin facturas"
msgid "Nothing to list yet." msgid "Nothing to list yet."
msgstr "Aún no hay nada que listar." msgstr "Aún no hay nada que listar."
#, fuzzy
msgid "Add your first bill" msgid "Add your first bill"
msgstr "Añade tu primera factura" msgstr "agregar una factura"
#, fuzzy
msgid "Add the first participant" msgid "Add the first participant"
msgstr "Agregar el primer participante" msgstr "Editar este participante"
msgid "Password reminder" msgid "Password reminder"
msgstr "Recordar contraseña" msgstr "Recordar contraseña"
@ -898,7 +910,7 @@ msgid "Invite people to join this project"
msgstr "Invita a personas a unirse a este proyecto" msgstr "Invita a personas a unirse a este proyecto"
msgid "Share an invitation link" msgid "Share an invitation link"
msgstr "Compartir un enlace de invitación" msgstr ""
msgid "" msgid ""
"The easiest way to invite people is to give them the following invitation" "The easiest way to invite people is to give them the following invitation"
@ -906,29 +918,26 @@ msgid ""
" add/edit/delete bills. However, they will not have access to important " " add/edit/delete bills. However, they will not have access to important "
"settings such as changing the private code or deleting the whole project." "settings such as changing the private code or deleting the whole project."
msgstr "" msgstr ""
"La forma más sencilla de invitar personas es dándoles el siguiente enlace de "
"invitación.<br />Podrán acceder al proyecto, administrar participantes, "
"agregar/editar/eliminar facturas. Sin embargo, no tendrán acceso a "
"configuraciones importantes como cambiar el código privado o eliminar todo "
"el proyecto."
msgid "Scan QR code" msgid "Scan QR code"
msgstr "Escanear código QR" msgstr ""
msgid "Use a mobile device with a compatible app." msgid "Use a mobile device with a compatible app."
msgstr "Utilice un dispositivo móvil con una aplicación compatible." msgstr ""
msgid "Send via Emails" msgid "Send via Emails"
msgstr "Enviar por correo electrónico" msgstr "Enviar por correo electrónico"
#, fuzzy
msgid "" msgid ""
"Specify a list of email adresses (separated by comma) of people you want " "Specify a list of email adresses (separated by comma) of people you want "
"to notify about the creation of this project. We will send them an email " "to notify about the creation of this project. We will send them an email "
"with the invitation link." "with the invitation link."
msgstr "" msgstr ""
"Especifique una lista de direcciones de correo electrónico (separadas por " "Especifique una lista (separada por comas) de las direcciones de correo "
"comas) de las personas a las que desea notificar sobre la creación de este " "electrónico a las que desea notificar acerca de la\n"
"proyecto. Les enviaremos un correo electrónico con el enlace de invitación." "creación de este proyecto de gestión presupuestaria y les enviaremos un "
"correo electrónico para usted."
msgid "Share Identifier & code" msgid "Share Identifier & code"
msgstr "Compartir identificador y código" msgstr "Compartir identificador y código"
@ -939,20 +948,16 @@ msgid ""
"to the full project, including changing settings such as the private code" "to the full project, including changing settings such as the private code"
" or project email address, or even deleting the whole project." " or project email address, or even deleting the whole project."
msgstr "" msgstr ""
"Puede compartir el identificador del proyecto y el código privado por "
"cualquier medio de comunicación.<br />Cualquier persona con el código "
"privado tendrá acceso al proyecto completo, incluido el cambio de "
"configuraciones como el código privado o la dirección de correo electrónico "
"del proyecto, o incluso la eliminación completa. proyecto."
msgid "Identifier:" msgid "Identifier:"
msgstr "Identificador:" msgstr "Identificador:"
#, fuzzy
msgid "Private code:" msgid "Private code:"
msgstr "Código privado:" msgstr "Código privado"
msgid "the private code was defined when you created the project" msgid "the private code was defined when you created the project"
msgstr "el código privado se definió cuando creaste el proyecto" msgstr ""
msgid "Who pays?" msgid "Who pays?"
msgstr "¿Quién paga?" msgstr "¿Quién paga?"
@ -1165,3 +1170,4 @@ msgstr "Período"
#~ "Puedes compartir directamente el siguiente " #~ "Puedes compartir directamente el siguiente "
#~ "enlace a través de tu medio " #~ "enlace a través de tu medio "
#~ "preferido" #~ "preferido"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-05-23 03:01+0000\n" "PO-Revision-Date: 2023-03-19 21:40+0000\n"
"Last-Translator: Yamin Siahmargooei <yamin8000@yahoo.com>\n" "Last-Translator: Sai Mohammad-Hossein Emami <emami@outlook.com>\n"
"Language-Team: Persian <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/fa/>\n"
"Language: fa\n" "Language: fa\n"
"Language-Team: Persian <https://hosted.weblate.org/projects/i-hate-"
"money/i-hate-money/fa/>\n"
"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 5.6-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -125,16 +125,16 @@ msgid "Reset password"
msgstr "بازنشانی گذرواژه" msgstr "بازنشانی گذرواژه"
msgid "When?" msgid "When?"
msgstr "چه زمانی؟" msgstr ""
msgid "What?" msgid "What?"
msgstr "چی؟" msgstr "چی؟"
msgid "Who paid?" msgid "Who paid?"
msgstr "چه کسی پرداخت کرد؟" msgstr ""
msgid "How much?" msgid "How much?"
msgstr "چقدر؟" msgstr ""
msgid "Currency" msgid "Currency"
msgstr "واحد پولی" msgstr "واحد پولی"
@ -180,14 +180,14 @@ msgid "People to notify"
msgstr "افرادی که براشون نوتیفیکیشن ارسال میشه" msgstr "افرادی که براشون نوتیفیکیشن ارسال میشه"
msgid "Send the invitations" msgid "Send the invitations"
msgstr "ارسال دعوت نامه ها" msgstr ""
#, python-format #, python-format
msgid "The email %(email)s is not valid" msgid "The email %(email)s is not valid"
msgstr "ایمیل %(email)s نامعتبره" msgstr "ایمیل %(email)s نامعتبره"
msgid "Logout" msgid "Logout"
msgstr "خروج" msgstr ""
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "" msgstr ""
@ -782,7 +782,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"
@ -1092,3 +1092,4 @@ msgstr ""
#~ "them an email with the invitation " #~ "them an email with the invitation "
#~ "link." #~ "link."
#~ msgstr "" #~ msgstr ""

Binary file not shown.

View file

@ -824,7 +824,7 @@ msgstr "Application mobile"
msgid "Documentation" msgid "Documentation"
msgstr "Documentation" msgstr "Documentation"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Panneau d'administration" msgstr "Panneau d'administration"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -788,7 +788,7 @@ msgstr "יישום לנייד"
msgid "Documentation" msgid "Documentation"
msgstr "דוקומנטציה" msgstr "דוקומנטציה"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -829,7 +829,7 @@ msgstr "मोबाइल एप्लीकेशन"
msgid "Documentation" msgid "Documentation"
msgstr "प्रलेखन" msgstr "प्रलेखन"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "व्यवस्थापन डैशबोर्ड" msgstr "व्यवस्थापन डैशबोर्ड"
#, fuzzy #, fuzzy

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -812,7 +812,7 @@ msgstr "Aplikasi Gawai"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentasi" msgstr "Dokumentasi"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Dasbor Administrasi" msgstr "Dasbor Administrasi"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -817,7 +817,7 @@ msgstr "Applicazione mobile"
msgid "Documentation" msgid "Documentation"
msgstr "Documentazione" msgstr "Documentazione"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Cruscotto Amministrazione" msgstr "Cruscotto Amministrazione"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-06-24 15:09+0000\n" "PO-Revision-Date: 2020-11-11 16:28+0000\n"
"Last-Translator: Khang Tran <tranchikhang@outlook.com>\n" "Last-Translator: Jwen921 <yangjingwen0921@gmail.com>\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/ja/>\n"
"Language: ja\n" "Language: ja\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/i-hate-"
"money/i-hate-money/ja/>\n"
"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 5.6-rc\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -27,17 +27,19 @@ msgstr "無効な入力です。数字と「+ - * / 」の演算子しか入力
msgid "Project name" msgid "Project name"
msgstr "プロジェクトの名前" msgstr "プロジェクトの名前"
#, fuzzy
msgid "Current private code" msgid "Current private code"
msgstr "現在の暗証コード" msgstr "暗証コード"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "プロジェクトを編集するために、暗証コードを入力してください" msgstr ""
#, fuzzy
msgid "New private code" msgid "New private code"
msgstr "暗証コード" msgstr "暗証コード"
msgid "Enter a new code if you want to change it" msgid "Enter a new code if you want to change it"
msgstr "変更するために、新しい暗証コードを入力してください" msgstr ""
msgid "Email" msgid "Email"
msgstr "メールアドレス" msgstr "メールアドレス"
@ -52,13 +54,15 @@ msgid "Default Currency"
msgstr "初期設定にする通貨" msgstr "初期設定にする通貨"
msgid "Setting a default currency enables currency conversion between bills" msgid "Setting a default currency enables currency conversion between bills"
msgstr "明細通貨変換のため、デフォルトの通貨を設定してください" msgstr ""
#, fuzzy
msgid "Unknown error" msgid "Unknown error"
msgstr "不明エラー" msgstr "未知のプロジェクト"
#, fuzzy
msgid "Invalid private code." msgid "Invalid private code."
msgstr "無効な暗証コード" msgstr "暗証コード"
msgid "" msgid ""
"This project cannot be set to 'no currency' because it contains bills in " "This project cannot be set to 'no currency' because it contains bills in "
@ -175,7 +179,7 @@ msgid "This project already have this participant"
msgstr "プロジェクトはすでにこのメンバーを含めています" msgstr "プロジェクトはすでにこのメンバーを含めています"
msgid "People to notify" msgid "People to notify"
msgstr "通知したい人" msgstr ""
msgid "Send the invitations" msgid "Send the invitations"
msgstr "招待状を送る" msgstr "招待状を送る"
@ -190,12 +194,11 @@ msgstr "ログアウト"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "" msgstr ""
#, python-format #, fuzzy, python-format
msgid "" msgid ""
"Please check the email configuration of the server or contact the " "Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s" "administrator: %(admin_email)s"
msgstr "申し訳ございませんが、エラーが発生しました。メールアドレスを再度チェックする" msgstr "申し訳ございませんが、招待メールを送ったとき、エラーが発生しました。メールアドレスを再度チェックするかまたは管理者に連絡ください。"
"か、または管理者( %(admin_email)sに連絡ください"
#. List with two items only #. List with two items only
msgid "{dual_object_0} and {dual_object_1}" msgid "{dual_object_0} and {dual_object_1}"
@ -268,7 +271,7 @@ msgid "Project settings have been changed successfully."
msgstr "" msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "CSVを読み込むことができません" msgstr ""
#, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
@ -289,7 +292,7 @@ msgid "Error deleting project"
msgstr "" msgstr ""
msgid "Unable to logout" msgid "Unable to logout"
msgstr "ログアウトできません" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -350,8 +353,9 @@ msgstr ""
msgid "Error deleting project history" msgid "Error deleting project history"
msgstr "プロジェクトの歴史を有効にする" msgstr "プロジェクトの歴史を有効にする"
#, fuzzy
msgid "Deleted project history." msgid "Deleted project history."
msgstr "プロジェクトの歴史を削除しました。" msgstr "プロジェクトの歴史を有効にする"
#, fuzzy #, fuzzy
msgid "Error deleting recorded IP addresses" msgid "Error deleting recorded IP addresses"
@ -570,8 +574,9 @@ msgstr ""
msgid "This project has history disabled. New actions won't appear below." msgid "This project has history disabled. New actions won't appear below."
msgstr "" msgstr ""
#, fuzzy
msgid "You can enable history on the settings page." msgid "You can enable history on the settings page."
msgstr "設定ページでIPアドレス記録を編集可能にすることができる" msgstr "設定ページでIPアドレス記録を編集可能にすることができる"
msgid "" msgid ""
"The table below reflects actions recorded prior to disabling project " "The table below reflects actions recorded prior to disabling project "
@ -617,13 +622,13 @@ msgstr "設定ページでIPアドレス記録を編集不可にすることが
msgid "From IP" msgid "From IP"
msgstr "IPから" msgstr "IPから"
#, python-format #, fuzzy, python-format
msgid "Project %(name)s added" msgid "Project %(name)s added"
msgstr "プロジェクト%(name)sが作成されました" msgstr "プロジェクトの名前"
#, python-format #, fuzzy, python-format
msgid "Bill %(name)s added" msgid "Bill %(name)s added"
msgstr "%(name)s明細が追加されました" msgstr "明細が追加されました"
#, python-format #, python-format
msgid "Participant %(name)s added" msgid "Participant %(name)s added"
@ -636,9 +641,9 @@ msgstr "プロジェクトの私用コードが変更された"
msgid "Project renamed to %(new_project_name)s" msgid "Project renamed to %(new_project_name)s"
msgstr "プロジェクト名は%(new_project_name)s" msgstr "プロジェクト名は%(new_project_name)s"
#, python-format #, fuzzy, python-format
msgid "Project contact email changed to %(new_email)s" msgid "Project contact email changed to %(new_email)s"
msgstr "プロジェクトの連絡メールが%(new_email)sに変更されました" msgstr "プロジェクトの連絡メールが…に変更された"
msgid "Project settings modified" msgid "Project settings modified"
msgstr "プロジェクトの設定が修正された" msgstr "プロジェクトの設定が修正された"
@ -676,9 +681,9 @@ msgstr "日付"
msgid "Amount in %(currency)s" msgid "Amount in %(currency)s"
msgstr "%(currency)sでの金額" msgstr "%(currency)sでの金額"
#, python-format #, fuzzy, python-format
msgid "Bill %(name)s modified" msgid "Bill %(name)s modified"
msgstr "%(name)s明細が変更されました" msgstr "明細が変更されました"
#, python-format #, python-format
msgid "Participant %(name)s modified" msgid "Participant %(name)s modified"
@ -692,17 +697,17 @@ msgstr "ユーザー%(name)sが既に取り除かれました"
msgid "Participant %(name)s removed" msgid "Participant %(name)s removed"
msgstr "ユーザー%(name)sが既に取り除かれました" msgstr "ユーザー%(name)sが既に取り除かれました"
#, python-format #, fuzzy, python-format
msgid "Project %(name)s changed in an unknown way" msgid "Project %(name)s changed in an unknown way"
msgstr "プロジェクト%(name)sが不明な方法で変更されました" msgstr "未知の方法で変更された"
#, python-format #, fuzzy, python-format
msgid "Bill %(name)s changed in an unknown way" msgid "Bill %(name)s changed in an unknown way"
msgstr "明細%(name)sが不明な方法で変更されました" msgstr "未知の方法で変更された"
#, python-format #, fuzzy, python-format
msgid "Participant %(name)s changed in an unknown way" msgid "Participant %(name)s changed in an unknown way"
msgstr "参加者%(name)sが不明な方法で変更されました" msgstr "未知の方法で変更された"
msgid "Nothing to list" msgid "Nothing to list"
msgstr "表示できるものがない" msgstr "表示できるものがない"
@ -797,7 +802,7 @@ msgstr "携帯アプリ"
msgid "Documentation" msgid "Documentation"
msgstr "書類" msgstr "書類"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "管理ダッシュボード" msgstr "管理ダッシュボード"
#, fuzzy #, fuzzy
@ -899,12 +904,14 @@ msgstr ""
msgid "Send via Emails" msgid "Send via Emails"
msgstr "メールで送る" msgstr "メールで送る"
#, fuzzy
msgid "" msgid ""
"Specify a list of email adresses (separated by comma) of people you want " "Specify a list of email adresses (separated by comma) of people you want "
"to notify about the creation of this project. We will send them an email " "to notify about the creation of this project. We will send them an email "
"with the invitation link." "with the invitation link."
msgstr "知らせたいメールアドレスのリスト(カンマ区切り)を指定してください。メールで" msgstr ""
"招待リンクを送信します。" "…を知らせたいメールアドレスのリストを特定する(カンマ区切り)\n"
"彼らにこの予算管理プロジェクトの作成をメールでお知らせします。"
msgid "Share Identifier & code" msgid "Share Identifier & code"
msgstr "名前とコードを共有する" msgstr "名前とコードを共有する"
@ -919,8 +926,9 @@ msgstr ""
msgid "Identifier:" msgid "Identifier:"
msgstr "名前:" msgstr "名前:"
#, fuzzy
msgid "Private code:" msgid "Private code:"
msgstr "暗証コード" msgstr "暗証コード"
msgid "the private code was defined when you created the project" msgid "the private code was defined when you created the project"
msgstr "" msgstr ""
@ -1101,3 +1109,4 @@ msgstr "期間"
#~ msgid "You can directly share the following link via your prefered medium" #~ msgid "You can directly share the following link via your prefered medium"
#~ msgstr "好きの手段で以下のリンクを直接に共有できる" #~ msgstr "好きの手段で以下のリンクを直接に共有できる"

Binary file not shown.

View file

@ -793,7 +793,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -793,7 +793,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -855,7 +855,7 @@ msgstr "Mobilprogram"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentasjon" msgstr "Dokumentasjon"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Administrasjonsoversiktspanel" msgstr "Administrasjonsoversiktspanel"
#, fuzzy #, fuzzy

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-03-17 12:01+0000\n" "PO-Revision-Date: 2021-02-17 02:50+0000\n"
"Last-Translator: Xander Jennie <xanderjennie21@gmail.com>\n" "Last-Translator: Sander Kooijmans <weblate@gogognome.nl>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/nl/>\n"
"Language: nl\n" "Language: nl\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/i-hate-money/i"
"-hate-money/nl/>\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.5-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -31,17 +31,19 @@ msgstr ""
msgid "Project name" msgid "Project name"
msgstr "Projectnaam" msgstr "Projectnaam"
#, fuzzy
msgid "Current private code" msgid "Current private code"
msgstr "Huidige Privécode" msgstr "Privécode"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "Voer het huidige privécode in om dit project te bewerken" msgstr ""
#, fuzzy
msgid "New private code" msgid "New private code"
msgstr "Nieuwe privécode" msgstr "Privécode"
msgid "Enter a new code if you want to change it" msgid "Enter a new code if you want to change it"
msgstr "Voer een nieuwe code in als u deze wilt wijzigen" msgstr ""
msgid "Email" msgid "Email"
msgstr "E-mailadres" msgstr "E-mailadres"
@ -57,24 +59,22 @@ msgstr "Standaard munteenheid"
msgid "Setting a default currency enables currency conversion between bills" msgid "Setting a default currency enables currency conversion between bills"
msgstr "" msgstr ""
"Door een standaardvaluta in te stellen, is valutaconversie tussen facturen "
"mogelijk"
#, fuzzy
msgid "Unknown error" msgid "Unknown error"
msgstr "Onbekende fout" msgstr "Onbekend project"
#, fuzzy
msgid "Invalid private code." msgid "Invalid private code."
msgstr "Ongeldige privécode." msgstr "Privécode"
msgid "" msgid ""
"This project cannot be set to 'no currency' because it contains bills in " "This project cannot be set to 'no currency' because it contains bills in "
"multiple currencies." "multiple currencies."
msgstr "" msgstr ""
"Dit project kan niet op 'geen valuta' worden gezet, omdat het facturen in "
"meerdere valuta's bevat."
msgid "Compatible with Cospend" msgid "Compatible with Cospend"
msgstr "Compatibel met mede besteden" msgstr ""
msgid "Project identifier" msgid "Project identifier"
msgstr "Project-id" msgstr "Project-id"
@ -92,16 +92,17 @@ msgid ""
msgstr "Er is al een project genaamd (\"%(project)s\"). Kies een andere naam" msgstr "Er is al een project genaamd (\"%(project)s\"). Kies een andere naam"
msgid "Which is a real currency: Euro or Petro dollar?" msgid "Which is a real currency: Euro or Petro dollar?"
msgstr "Wat is een echte valuta: de euro of de petro-dollar?" msgstr ""
#, fuzzy
msgid "euro" msgid "euro"
msgstr "euro" msgstr "Periode"
msgid "Please, validate the captcha to proceed." msgid "Please, validate the captcha to proceed."
msgstr "Valideer de captcha om door te gaan." msgstr ""
msgid "Enter private code to confirm deletion" msgid "Enter private code to confirm deletion"
msgstr "Voer de privécode in om de verwijder opdracht te bevestigen" msgstr ""
msgid "Get in" msgid "Get in"
msgstr "Inloggen" msgstr "Inloggen"
@ -125,7 +126,7 @@ msgid "Password confirmation"
msgstr "Wachtwoord bevestigen" msgstr "Wachtwoord bevestigen"
msgid "Reset password" msgid "Reset password"
msgstr "Wachtwoord opnieuw instellen" msgstr "Wachtwoord herstellen"
msgid "When?" msgid "When?"
msgstr "Wanneer?" msgstr "Wanneer?"
@ -173,14 +174,16 @@ msgstr "Gewicht"
msgid "Add" msgid "Add"
msgstr "Toevoegen" msgstr "Toevoegen"
#, fuzzy
msgid "The participant name is invalid" msgid "The participant name is invalid"
msgstr "De naam van de deelnemer is ongeldig" msgstr "De gebruiker '%(name)s' is verwijderd"
#, fuzzy
msgid "This project already have this participant" msgid "This project already have this participant"
msgstr "Deze deelnemer is al lid van het project" msgstr "Deze deelnemer is al lid van het project"
msgid "People to notify" msgid "People to notify"
msgstr "Mensen om op de hoogte te stellen" msgstr ""
msgid "Send the invitations" msgid "Send the invitations"
msgstr "Uitnodigingen versturen" msgstr "Uitnodigingen versturen"
@ -193,52 +196,54 @@ msgid "Logout"
msgstr "Uitloggen" msgstr "Uitloggen"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "Controleer de e-mailconfiguratie van de server." msgstr ""
#, python-format #, fuzzy, python-format
msgid "" msgid ""
"Please check the email configuration of the server or contact the " "Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s" "administrator: %(admin_email)s"
msgstr "" msgstr ""
"Controleer de e-mailinstellingen van de server of neem contact op met de " "Sorry, er is iets fout gegaan bij het verzenden van de uitnodigingsmails."
"beheerder: %(admin_email)s" " Controleer de e-mailinstellingen van de server of neem contact op met de"
" beheerder."
#. List with two items only #. List with two items only
msgid "{dual_object_0} and {dual_object_1}" msgid "{dual_object_0} and {dual_object_1}"
msgstr "{dual_object_0} en {dual_object_1}" msgstr ""
#. Last two items of a list with more than 3 items #. Last two items of a list with more than 3 items
msgid "{previous_object}, and {end_object}" msgid "{previous_object}, and {end_object}"
msgstr "{previous_object} en{end_object}" msgstr ""
#. Two items in a middle of a list with more than 5 objects #. Two items in a middle of a list with more than 5 objects
msgid "{previous_object}, {next_object}" msgid "{previous_object}, {next_object}"
msgstr "{previous_object}, {next_object}" msgstr ""
#. First two items of a list with more than 3 items #. First two items of a list with more than 3 items
msgid "{start_object}, {next_object}" msgid "{start_object}, {next_object}"
msgstr "{start_object}, {next_object}" msgstr ""
msgid "No Currency" msgid "No Currency"
msgstr "Geen valuta" msgstr "Geen munteenheid"
#. Form error with only one error #. Form error with only one error
msgid "{prefix}: {error}" msgid "{prefix}: {error}"
msgstr "{prefix}: {error}" msgstr ""
#. Form error with a list of errors #. Form error with a list of errors
msgid "{prefix}:<br />{errors}" msgid "{prefix}:<br />{errors}"
msgstr "{prefix}:<br />{errors}" msgstr ""
#, fuzzy
msgid "Too many failed login attempts." msgid "Too many failed login attempts."
msgstr "Te veel mislukte inlogpogingen." msgstr "Te vaak onjuist ingelogd. Probeer het later opnieuw."
#, python-format #, python-format
msgid "This admin password is not the right one. Only %(num)d attempts left." msgid "This admin password is not the right one. Only %(num)d attempts left."
msgstr "Het admin-wachtwoord is onjuist. Je kunt het nog %(num)d keer proberen." msgstr "Het admin-wachtwoord is onjuist. Je kunt het nog %(num)d keer proberen."
msgid "Provided token is invalid" msgid "Provided token is invalid"
msgstr "Het opgegeven token is ongeldig" msgstr ""
msgid "This private code is not the right one" msgid "This private code is not the right one"
msgstr "Deze privécode is onjuist" msgstr "Deze privécode is onjuist"
@ -253,12 +258,14 @@ msgstr ""
"We hebben geprobeerd een herinneringsmail te versturen, maar er is iets " "We hebben geprobeerd een herinneringsmail te versturen, maar er is iets "
"fout gegaan. Je kunt het project nog steeds normaal gebruiken." "fout gegaan. Je kunt het project nog steeds normaal gebruiken."
#, fuzzy
msgid "" msgid ""
"Sorry, there was an error while sending you an email with password reset " "Sorry, there was an error while sending you an email with password reset "
"instructions." "instructions."
msgstr "" msgstr ""
"Sorry, er is iets fout gegaan bij het verzenden van een e-mail met " "Sorry, er is iets fout gegaan bij het verzenden van een e-mail met "
"instructies om je wachtwoord te herstellen." "instructies om je wachtwoord te herstellen. Controleer de "
"e-mailinstellingen van de server of neem contact op met de beheerder."
msgid "No token provided" msgid "No token provided"
msgstr "Geen toegangssleutel opgegeven" msgstr "Geen toegangssleutel opgegeven"
@ -276,11 +283,11 @@ msgid "Project settings have been changed successfully."
msgstr "" msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "Kan CSV niet parseren" msgstr ""
#, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
msgstr "Ontbrekende attribute: %(attribute)s" msgstr ""
msgid "" msgid ""
"Cannot add bills in multiple currencies to a project without default " "Cannot add bills in multiple currencies to a project without default "
@ -297,7 +304,7 @@ msgid "Error deleting project"
msgstr "" msgstr ""
msgid "Unable to logout" msgid "Unable to logout"
msgstr "Kan niet uitloggen" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -347,10 +354,10 @@ msgid "The bill has been added"
msgstr "De rekening is toegevoegd" msgstr "De rekening is toegevoegd"
msgid "Error deleting bill" msgid "Error deleting bill"
msgstr "Fout bij verwijderen factuur" msgstr ""
msgid "The bill has been deleted" msgid "The bill has been deleted"
msgstr "De factuur is verwijderd" msgstr "De rekening is verwijderd"
msgid "The bill has been modified" msgid "The bill has been modified"
msgstr "De rekening is aangepast" msgstr "De rekening is aangepast"
@ -386,8 +393,9 @@ msgstr "Terug naar de lijst"
msgid "Administration tasks are currently disabled." msgid "Administration tasks are currently disabled."
msgstr "Beheerderstaken zijn momenteel uitgeschakeld." msgstr "Beheerderstaken zijn momenteel uitgeschakeld."
#, fuzzy
msgid "Authentication" msgid "Authentication"
msgstr "Authenticatie" msgstr "Documentatie"
msgid "The project you are trying to access do not exist, do you want to" msgid "The project you are trying to access do not exist, do you want to"
msgstr "Het project dat je probeert te benaderen bestaat niet. Wil je" msgstr "Het project dat je probeert te benaderen bestaat niet. Wil je"
@ -404,17 +412,18 @@ msgstr "Nieuw project aanmaken"
msgid "Project" msgid "Project"
msgstr "Project" msgstr "Project"
#, fuzzy
msgid "Number of participants" msgid "Number of participants"
msgstr "Aantal deelnemers" msgstr "deelnemers toevoegen"
msgid "Number of bills" msgid "Number of bills"
msgstr "Aantal facturen" msgstr "Aantal rekeningen"
msgid "Newest bill" msgid "Newest bill"
msgstr "Nieuwste factuur" msgstr "Nieuwste rekening"
msgid "Oldest bill" msgid "Oldest bill"
msgstr "Oudste factuur" msgstr "Oudste rekening"
msgid "Actions" msgid "Actions"
msgstr "Acties" msgstr "Acties"
@ -422,8 +431,9 @@ msgstr "Acties"
msgid "edit" msgid "edit"
msgstr "bewerken" msgstr "bewerken"
#, fuzzy
msgid "Delete project" msgid "Delete project"
msgstr "Project verwijderen" msgstr "Project aanpassen"
msgid "show" msgid "show"
msgstr "tonen" msgstr "tonen"
@ -431,8 +441,9 @@ msgstr "tonen"
msgid "The Dashboard is currently deactivated." msgid "The Dashboard is currently deactivated."
msgstr "De overzichtspagina is momenteel uitgeschakeld." msgstr "De overzichtspagina is momenteel uitgeschakeld."
#, fuzzy
msgid "Download Mobile Application" msgid "Download Mobile Application"
msgstr "Mobiele applicatie downloaden" msgstr "Mobiele app"
#, fuzzy #, fuzzy
msgid "Get it on" msgid "Get it on"
@ -441,8 +452,9 @@ msgstr "Inloggen"
msgid "Edit project" msgid "Edit project"
msgstr "Project aanpassen" msgstr "Project aanpassen"
#, fuzzy
msgid "Import project" msgid "Import project"
msgstr "Project importeren" msgstr "Project aanpassen"
msgid "Download project's data" msgid "Download project's data"
msgstr "Projectgegevens downloaden" msgstr "Projectgegevens downloaden"
@ -814,7 +826,7 @@ msgstr "Mobiele app"
msgid "Documentation" msgid "Documentation"
msgstr "Documentatie" msgstr "Documentatie"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Administratie-overzicht" msgstr "Administratie-overzicht"
#, fuzzy #, fuzzy
@ -1151,3 +1163,4 @@ msgstr "Periode"
#~ msgid "You can directly share the following link via your prefered medium" #~ msgid "You can directly share the following link via your prefered medium"
#~ msgstr "Je kunt de volgende link direct delen" #~ msgstr "Je kunt de volgende link direct delen"

View file

@ -1,932 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-15 16:06+0200\n"
"PO-Revision-Date: 2024-07-03 19:09+0000\n"
"Last-Translator: Quentin PAGÈS <quentinantonin@free.fr>\n"
"Language-Team: Occitan <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/oc/>\n"
"Language: oc\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.7-dev\n"
#, python-format
msgid "You have just created '%(project)s' to share your expenses"
msgstr "Venètz de crear « %(project)s » per despartir vòstras despensas"
msgid ""
"Not a valid amount or expression. Only numbers and + - * / operators are "
"accepted."
msgstr ""
"Quantitat o expression non valida. Sonque los nombre e operator +-*/ son "
"acceptats."
msgid "Project name"
msgstr "Nom del projècte"
msgid "Current private code"
msgstr "Còdi daccès actual"
msgid "Enter existing private code to edit project"
msgstr "Picatz lo còdi daccès existissent per editar lo projècte"
msgid "New private code"
msgstr "Còdi privat novèl"
msgid "Enter a new code if you want to change it"
msgstr "Picatz lo còdi novèl se lo volètz cambiar"
msgid "Email"
msgstr "Adreça electronica"
msgid "Enable project history"
msgstr "Activar listoric de projècte"
msgid "Use IP tracking for project history"
msgstr "Reculhir las adreças IP dins listoric de projècte"
msgid "Default Currency"
msgstr "Moneda predeterminada"
msgid "Setting a default currency enables currency conversion between bills"
msgstr ""
msgid "Unknown error"
msgstr "Error desconeguda"
msgid "Invalid private code."
msgstr "Còdi daccès invalid."
msgid ""
"This project cannot be set to 'no currency' because it contains bills in "
"multiple currencies."
msgstr ""
msgid "Compatible with Cospend"
msgstr "Compatible amb Cospend"
msgid "Project identifier"
msgstr "Identificant del projècte"
msgid "Private code"
msgstr "Còdi privat"
msgid "Create the project"
msgstr "Crear lo projècte"
#, python-format
msgid ""
"A project with this identifier (\"%(project)s\") already exists. Please "
"choose a new identifier"
msgstr ""
msgid "Which is a real currency: Euro or Petro dollar?"
msgstr ""
msgid "euro"
msgstr "èuro"
msgid "Please, validate the captcha to proceed."
msgstr "Se vos plai, validatz el captcha per contunhar."
msgid "Enter private code to confirm deletion"
msgstr "Picatz lo còdi daccès per confirmar la supression"
msgid "Get in"
msgstr "Dintrar"
msgid "Admin password"
msgstr "Senhal dadministracion"
msgid "Send me the code by email"
msgstr "Mandatz-me lo còdi per corrièl"
msgid "This project does not exists"
msgstr "Aqueste projècte existís pas"
msgid "Password mismatch"
msgstr ""
msgid "Password"
msgstr "Senhal"
msgid "Password confirmation"
msgstr "Confirmacion del senhla"
msgid "Reset password"
msgstr "Reïnicializar lo senhal"
msgid "When?"
msgstr "Quand ?"
msgid "What?"
msgstr "Qué ?"
msgid "Who paid?"
msgstr "Qual paguèt ?"
msgid "How much?"
msgstr "Quant ?"
msgid "Currency"
msgstr "Moneda"
msgid "External link"
msgstr "Ligam extèrne"
msgid "A link to an external document, related to this bill"
msgstr ""
msgid "For whom?"
msgstr "Per qual ?"
msgid "Submit"
msgstr "Enviar"
msgid "Submit and add a new one"
msgstr ""
#, python-format
msgid "Project default: %(currency)s"
msgstr ""
msgid "Name"
msgstr "Nom"
msgid "Weights should be positive"
msgstr ""
msgid "Weight"
msgstr "Pes"
msgid "Add"
msgstr "Apondre"
msgid "The participant name is invalid"
msgstr "Lo nom del participant es incorrècte"
msgid "This project already have this participant"
msgstr "Aqueste projècte a ja aqueste participant"
msgid "People to notify"
msgstr "Personas de convidar"
msgid "Send the invitations"
msgstr "Enviar las invitacions"
#, python-format
msgid "The email %(email)s is not valid"
msgstr ""
msgid "Logout"
msgstr "Desconnexion"
msgid "Please check the email configuration of the server."
msgstr ""
#, python-format
msgid ""
"Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s"
msgstr ""
#. List with two items only
msgid "{dual_object_0} and {dual_object_1}"
msgstr ""
#. Last two items of a list with more than 3 items
msgid "{previous_object}, and {end_object}"
msgstr ""
#. Two items in a middle of a list with more than 5 objects
msgid "{previous_object}, {next_object}"
msgstr "{previous_object}, {next_object}"
#. First two items of a list with more than 3 items
msgid "{start_object}, {next_object}"
msgstr "{start_object}, {next_object}"
msgid "No Currency"
msgstr "Cap de moneda"
#. Form error with only one error
msgid "{prefix}: {error}"
msgstr "{prefix} : {error}"
#. Form error with a list of errors
msgid "{prefix}:<br />{errors}"
msgstr "{prefix} :<br />{errors}"
msgid "Too many failed login attempts."
msgstr ""
#, python-format
msgid "This admin password is not the right one. Only %(num)d attempts left."
msgstr ""
msgid "Provided token is invalid"
msgstr "Aqueste geton es invalid"
msgid "This private code is not the right one"
msgstr ""
msgid "A reminder email has just been sent to you"
msgstr ""
msgid ""
"We tried to send you an reminder email, but there was an error. You can "
"still use the project normally."
msgstr ""
msgid ""
"Sorry, there was an error while sending you an email with password reset "
"instructions."
msgstr ""
msgid "No token provided"
msgstr ""
msgid "Invalid token"
msgstr "Geton invalid"
msgid "Unknown project"
msgstr ""
msgid "Password successfully reset."
msgstr ""
msgid "Project settings have been changed successfully."
msgstr ""
msgid "Unable to parse CSV"
msgstr ""
#, python-format
msgid "Missing attribute: %(attribute)s"
msgstr "Atribut mancant : %(attribute)s"
msgid ""
"Cannot add bills in multiple currencies to a project without default "
"currency"
msgstr ""
msgid "Project successfully uploaded"
msgstr ""
msgid "Project successfully deleted"
msgstr ""
msgid "Error deleting project"
msgstr ""
msgid "Unable to logout"
msgstr ""
#, python-format
msgid "You have been invited to share your expenses for %(project)s"
msgstr ""
msgid "Your invitations have been sent"
msgstr ""
msgid "Sorry, there was an error while trying to send the invitation emails."
msgstr ""
#, python-format
msgid "%(member)s has been added"
msgstr ""
msgid "Error activating participant"
msgstr ""
#, python-format
msgid "%(name)s is part of this project again"
msgstr ""
msgid "Error removing participant"
msgstr ""
#, python-format
msgid ""
"Participant '%(name)s' has been deactivated. It will still appear in the "
"list until its balance reach zero."
msgstr ""
#, python-format
msgid "Participant '%(name)s' has been removed"
msgstr ""
#, python-format
msgid "Participant '%(name)s' has been modified"
msgstr ""
msgid "The bill has been added"
msgstr ""
msgid "Error deleting bill"
msgstr ""
msgid "The bill has been deleted"
msgstr ""
msgid "The bill has been modified"
msgstr ""
#, python-format
msgid "%(lang)s is not a supported language"
msgstr ""
msgid "Error deleting project history"
msgstr ""
msgid "Deleted project history."
msgstr ""
msgid "Error deleting recorded IP addresses"
msgstr ""
msgid "Deleted recorded IP addresses in project history."
msgstr ""
msgid "Sorry, we were unable to find the page you've asked for."
msgstr ""
msgid "The best thing to do is probably to get back to the main page."
msgstr ""
msgid "Back to the list"
msgstr ""
msgid "Administration tasks are currently disabled."
msgstr ""
msgid "Authentication"
msgstr ""
msgid "The project you are trying to access do not exist, do you want to"
msgstr ""
msgid "create it"
msgstr ""
msgid "?"
msgstr ""
msgid "Create a new project"
msgstr ""
msgid "Project"
msgstr "Projècte"
msgid "Number of participants"
msgstr ""
msgid "Number of bills"
msgstr ""
msgid "Newest bill"
msgstr ""
msgid "Oldest bill"
msgstr ""
msgid "Actions"
msgstr ""
msgid "edit"
msgstr "modifica"
msgid "Delete project"
msgstr ""
msgid "show"
msgstr "mostrar"
msgid "The Dashboard is currently deactivated."
msgstr ""
msgid "Download Mobile Application"
msgstr ""
msgid "Get it on"
msgstr ""
msgid "Edit project"
msgstr ""
msgid "Import project"
msgstr ""
msgid "Download project's data"
msgstr ""
msgid "Bill items"
msgstr ""
msgid "Download the list of bills with owner, amount, reason,... "
msgstr ""
msgid "Settle plans"
msgstr ""
msgid "Download the list of transactions needed to settle the current bills."
msgstr ""
msgid "Can't remember the password?"
msgstr ""
msgid "Cancel"
msgstr "Anullar"
msgid "Privacy Settings"
msgstr "Paramètres de confidencialitat"
msgid "Save changes"
msgstr "Enregistrar las modificacions"
msgid "This will remove all bills and participants in this project!"
msgstr ""
msgid "Import previously exported project"
msgstr ""
msgid "Choose file"
msgstr ""
msgid "Edit this bill"
msgstr ""
msgid "Add a bill"
msgstr ""
msgid "Simple operations are allowed, e.g. (18+36.2)/3"
msgstr ""
msgid "Everyone"
msgstr "Tot lo monde"
msgid "No one"
msgstr "Degun"
msgid "More options"
msgstr "Mai d'opcions"
msgid "Add participant"
msgstr ""
msgid "Edit this participant"
msgstr ""
msgid "john.doe@example.com, mary.moe@site.com"
msgstr ""
msgid "Download"
msgstr ""
msgid "Disabled Project History"
msgstr ""
msgid "Disabled Project History & IP Address Recording"
msgstr ""
msgid "Enabled Project History"
msgstr ""
msgid "Disabled IP Address Recording"
msgstr ""
msgid "Enabled Project History & IP Address Recording"
msgstr ""
msgid "Enabled IP Address Recording"
msgstr ""
msgid "History Settings Changed"
msgstr ""
#, python-format
msgid "Bill %(name)s: %(property_name)s changed from %(before)s to %(after)s"
msgstr ""
#, python-format
msgid "Bill %(name)s: %(property_name)s changed to %(after)s"
msgstr ""
msgid "Confirm Remove IP Adresses"
msgstr ""
msgid ""
"Are you sure you want to delete all recorded IP addresses from this "
"project?\n"
" The rest of the project history will be unaffected. This "
"action cannot be undone."
msgstr ""
msgid "Confirm deletion"
msgstr "Confirmar la supression"
msgid "Close"
msgstr "Tampar"
msgid "Delete Confirmation"
msgstr ""
msgid ""
"Are you sure you want to erase all history for this project? This action "
"cannot be undone."
msgstr ""
#, python-format
msgid "Bill %(name)s: added %(owers_list_str)s to owers list"
msgstr ""
#, python-format
msgid "Bill %(name)s: removed %(owers_list_str)s from owers list"
msgstr ""
msgid "This project has history disabled. New actions won't appear below."
msgstr ""
msgid "You can enable history on the settings page."
msgstr ""
msgid ""
"The table below reflects actions recorded prior to disabling project "
"history."
msgstr ""
msgid "You can clear the project history to remove them."
msgstr ""
msgid ""
"Some entries below contain IP addresses, even though this project has IP "
"recording disabled. "
msgstr ""
msgid "Delete stored IP addresses"
msgstr ""
msgid "No IP Addresses to erase"
msgstr ""
msgid "Delete Stored IP Addresses"
msgstr ""
msgid "No history to erase"
msgstr ""
msgid "Clear Project History"
msgstr ""
msgid "Time"
msgstr ""
msgid "Event"
msgstr ""
msgid "IP address recording can be enabled on the settings page"
msgstr ""
msgid "IP address recording can be disabled on the settings page"
msgstr ""
msgid "From IP"
msgstr ""
#, python-format
msgid "Project %(name)s added"
msgstr ""
#, python-format
msgid "Bill %(name)s added"
msgstr ""
#, python-format
msgid "Participant %(name)s added"
msgstr ""
msgid "Project private code changed"
msgstr ""
#, python-format
msgid "Project renamed to %(new_project_name)s"
msgstr ""
#, python-format
msgid "Project contact email changed to %(new_email)s"
msgstr ""
msgid "Project settings modified"
msgstr ""
#, python-format
msgid "Participant %(name)s deactivated"
msgstr ""
#, python-format
msgid "Participant %(name)s reactivated"
msgstr ""
#, python-format
msgid "Participant %(name)s renamed to %(new_name)s"
msgstr ""
#, python-format
msgid "Bill %(name)s renamed to %(new_description)s"
msgstr ""
#, python-format
msgid "Participant %(name)s: weight changed from %(old_weight)s to %(new_weight)s"
msgstr ""
msgid "Payer"
msgstr "Pagaire"
msgid "Amount"
msgstr "Soma"
msgid "Date"
msgstr "Data"
#, python-format
msgid "Amount in %(currency)s"
msgstr ""
#, python-format
msgid "Bill %(name)s modified"
msgstr ""
#, python-format
msgid "Participant %(name)s modified"
msgstr ""
#, python-format
msgid "Bill %(name)s removed"
msgstr ""
#, python-format
msgid "Participant %(name)s removed"
msgstr ""
#, python-format
msgid "Project %(name)s changed in an unknown way"
msgstr ""
#, python-format
msgid "Bill %(name)s changed in an unknown way"
msgstr ""
#, python-format
msgid "Participant %(name)s changed in an unknown way"
msgstr ""
msgid "Nothing to list"
msgstr "Pas res a listar"
msgid "Someone probably cleared the project history."
msgstr ""
msgid "Manage your shared <br />expenses, easily"
msgstr ""
msgid "Try out the demo"
msgstr ""
msgid "You're sharing a house?"
msgstr ""
msgid "Going on holidays with friends?"
msgstr ""
msgid "Simply sharing money with others?"
msgstr ""
msgid "We can help!"
msgstr ""
msgid "Log in to an existing project"
msgstr ""
msgid "Log in"
msgstr "Connexion"
msgid "can't remember your password?"
msgstr ""
msgid "Create"
msgstr "Crear"
msgid ""
"Don\\'t reuse a personal password. Choose a private code and send it to "
"your friends"
msgstr ""
msgid "Account manager"
msgstr "Gestionari de compte"
msgid "Bills"
msgstr "Facturas"
msgid "Settle"
msgstr ""
msgid "Statistics"
msgstr "Estatisticas"
msgid "Languages"
msgstr "Lengas"
msgid "Projects"
msgstr "Projèctes"
msgid "Start a new project"
msgstr ""
msgid "History"
msgstr ""
msgid "Settings"
msgstr ""
msgid "RSS Feed"
msgstr ""
msgid "Other projects :"
msgstr ""
msgid "switch to"
msgstr ""
msgid "Dashboard"
msgstr "Panèl dadministracion"
#, python-format
msgid "Please retry after %(date)s."
msgstr ""
msgid "Code"
msgstr "Còdi"
msgid "Mobile Application"
msgstr ""
msgid "Documentation"
msgstr "Documentacion"
msgid "Administration Dashboard"
msgstr "Panèl dadministracion"
msgid "Legal information"
msgstr ""
msgid "\"I hate money\" is free software"
msgstr ""
msgid "you can contribute and improve it!"
msgstr ""
#, python-format
msgid "%(amount)s each"
msgstr "%(amount)s cadun"
msgid "you sure?"
msgstr "O volètz vertadièrament?"
msgid "Invite people"
msgstr "Convidar de monde"
msgid "Newer bills"
msgstr ""
msgid "Older bills"
msgstr ""
msgid "You should start by adding participants"
msgstr ""
msgid "Add a new bill"
msgstr ""
msgid "For what?"
msgstr "Per qué ?"
#, python-format
msgid "Added on %(date)s"
msgstr ""
#, python-format
msgid "Everyone but %(excluded)s"
msgstr ""
msgid "delete"
msgstr "suprimir"
msgid "No bills"
msgstr "Cap de factura"
msgid "Nothing to list yet."
msgstr "Pas res a listar pel moment."
msgid "Add your first bill"
msgstr ""
msgid "Add the first participant"
msgstr ""
msgid "Password reminder"
msgstr ""
msgid ""
"A link to reset your password has been sent to you, please check your "
"emails."
msgstr ""
msgid "Return to home page"
msgstr ""
msgid "Your projects"
msgstr ""
msgid "Reset your password"
msgstr ""
msgid "Invite people to join this project"
msgstr ""
msgid "Share an invitation link"
msgstr ""
msgid ""
"The easiest way to invite people is to give them the following invitation"
" link.<br />They will be able to access the project, manage participants,"
" add/edit/delete bills. However, they will not have access to important "
"settings such as changing the private code or deleting the whole project."
msgstr ""
msgid "Scan QR code"
msgstr ""
msgid "Use a mobile device with a compatible app."
msgstr ""
msgid "Send via Emails"
msgstr ""
msgid ""
"Specify a list of email adresses (separated by comma) of people you want "
"to notify about the creation of this project. We will send them an email "
"with the invitation link."
msgstr ""
msgid "Share Identifier & code"
msgstr ""
msgid ""
"You can share the project identifier and the private code by any "
"communication means.<br />Anyone with the private code will have access "
"to the full project, including changing settings such as the private code"
" or project email address, or even deleting the whole project."
msgstr ""
msgid "Identifier:"
msgstr ""
msgid "Private code:"
msgstr "Còdi privat :"
msgid "the private code was defined when you created the project"
msgstr ""
msgid "Who pays?"
msgstr "Qual paga ?"
msgid "To whom?"
msgstr "A qual ?"
msgid "Who?"
msgstr "Qual ?"
msgid "Balance"
msgstr ""
msgid "deactivate"
msgstr ""
msgid "reactivate"
msgstr ""
msgid "Paid"
msgstr "Pagat"
msgid "Spent"
msgstr "Despensat"
msgid "Expenses by Month"
msgstr "Despensas per mes"
msgid "Period"
msgstr "Periòde"

Binary file not shown.

View file

@ -812,7 +812,7 @@ msgstr "Aplikacja mobilna"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentacja" msgstr "Dokumentacja"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Kokpit administracyjny" msgstr "Kokpit administracyjny"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-05-10 07:26+0000\n" "PO-Revision-Date: 2023-05-05 00:47+0000\n"
"Last-Translator: Gesiane Pajarinen <gesianef@hotmail.com>\n" "Last-Translator: MurkBRA <anjo1077@gmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/pt/>\n"
"Language: pt\n" "Language: pt\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/i-hate-"
"money/i-hate-money/pt/>\n"
"Plural-Forms: nplurals=2; plural=n > 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.5.4-rc\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -34,7 +34,7 @@ msgid "Current private code"
msgstr "Código privado novo" msgstr "Código privado novo"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "Insira o código privado existente para editar o projeto" msgstr ""
msgid "New private code" msgid "New private code"
msgstr "Código privado novo" msgstr "Código privado novo"
@ -71,7 +71,7 @@ msgstr ""
" em várias moedas." " em várias moedas."
msgid "Compatible with Cospend" msgid "Compatible with Cospend"
msgstr "Compatível com Cospend" msgstr ""
msgid "Project identifier" msgid "Project identifier"
msgstr "Identificador do projeto" msgstr "Identificador do projeto"
@ -192,7 +192,7 @@ msgid "Logout"
msgstr "Sair" msgstr "Sair"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "Verifique a configuração de e-mail do servidor." msgstr ""
#, fuzzy, python-format #, fuzzy, python-format
msgid "" msgid ""
@ -279,14 +279,14 @@ msgid "Password successfully reset."
msgstr "Palavra-passe redefinida corretamente." msgstr "Palavra-passe redefinida corretamente."
msgid "Project settings have been changed successfully." msgid "Project settings have been changed successfully."
msgstr "As configurações do projeto foram alteradas com sucesso." msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "Não foi possível processar o CSV" msgstr ""
#, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
msgstr "Faltando o atributo: %(attribute)s" msgstr ""
msgid "" msgid ""
"Cannot add bills in multiple currencies to a project without default " "Cannot add bills in multiple currencies to a project without default "
@ -305,7 +305,7 @@ msgid "Error deleting project"
msgstr "Erro ao apagar o projeto" msgstr "Erro ao apagar o projeto"
msgid "Unable to logout" msgid "Unable to logout"
msgstr "Não é possível sair" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -365,7 +365,7 @@ msgstr "A fatura foi modificada"
#, python-format #, python-format
msgid "%(lang)s is not a supported language" msgid "%(lang)s is not a supported language"
msgstr "Não temos configuração para o idioma %(lang)s" msgstr ""
msgid "Error deleting project history" msgid "Error deleting project history"
msgstr "Erro ao apagar o histórico do projeto" msgstr "Erro ao apagar o histórico do projeto"
@ -476,7 +476,7 @@ msgid "Privacy Settings"
msgstr "Configurações de Privacidade" msgstr "Configurações de Privacidade"
msgid "Save changes" msgid "Save changes"
msgstr "Salvar as alterações" msgstr ""
msgid "This will remove all bills and participants in this project!" msgid "This will remove all bills and participants in this project!"
msgstr "Isso removerá todas as faturas e os participantes deste projeto!" msgstr "Isso removerá todas as faturas e os participantes deste projeto!"
@ -495,7 +495,7 @@ msgid "Add a bill"
msgstr "Adicionar uma fatura" msgstr "Adicionar uma fatura"
msgid "Simple operations are allowed, e.g. (18+36.2)/3" msgid "Simple operations are allowed, e.g. (18+36.2)/3"
msgstr "Operações simples são permitidas, por ex. (18+36,2)/3" msgstr ""
msgid "Everyone" msgid "Everyone"
msgstr "Todos" msgstr "Todos"
@ -587,7 +587,6 @@ msgstr "Fatura %(name)s: removido %(owers_list_str)s da lista de proprietários"
msgid "This project has history disabled. New actions won't appear below." msgid "This project has history disabled. New actions won't appear below."
msgstr "" msgstr ""
"Este projeto tem o histórico desativado. Novas ações não aparecerão abaixo."
#, fuzzy #, fuzzy
msgid "You can enable history on the settings page." msgid "You can enable history on the settings page."
@ -597,8 +596,6 @@ msgid ""
"The table below reflects actions recorded prior to disabling project " "The table below reflects actions recorded prior to disabling project "
"history." "history."
msgstr "" msgstr ""
"A tabela abaixo contém as ações registradas antes da desativação do "
"histórico do projeto."
#, fuzzy #, fuzzy
msgid "You can clear the project history to remove them." msgid "You can clear the project history to remove them."
@ -799,7 +796,7 @@ msgid "Settings"
msgstr "Configurações" msgstr "Configurações"
msgid "RSS Feed" msgid "RSS Feed"
msgstr "RSS Feed" msgstr ""
msgid "Other projects :" msgid "Other projects :"
msgstr "Outros projetos:" msgstr "Outros projetos:"
@ -812,7 +809,7 @@ msgstr "Painel de controle"
#, python-format #, python-format
msgid "Please retry after %(date)s." msgid "Please retry after %(date)s."
msgstr "Por favor, tente novamente após %(date)s." msgstr ""
msgid "Code" msgid "Code"
msgstr "Código" msgstr "Código"
@ -823,7 +820,7 @@ msgstr "Aplicação Mobile"
msgid "Documentation" msgid "Documentation"
msgstr "Documentação" msgstr "Documentação"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Painel de Administração" msgstr "Painel de Administração"
msgid "Legal information" msgid "Legal information"
@ -908,7 +905,7 @@ msgid "Invite people to join this project"
msgstr "Convide pessoas para participar deste projeto" msgstr "Convide pessoas para participar deste projeto"
msgid "Share an invitation link" msgid "Share an invitation link"
msgstr "Compartilhe um link do convite" msgstr ""
msgid "" msgid ""
"The easiest way to invite people is to give them the following invitation" "The easiest way to invite people is to give them the following invitation"
@ -916,17 +913,12 @@ msgid ""
" add/edit/delete bills. However, they will not have access to important " " add/edit/delete bills. However, they will not have access to important "
"settings such as changing the private code or deleting the whole project." "settings such as changing the private code or deleting the whole project."
msgstr "" msgstr ""
"A maneira mais fácil de convidar as pessoas é compartilhando o seguinte link "
"do convite.<br />Eles poderão acessar o projeto, gerenciar participantes, "
"adicionar/editar/excluir contas. No entanto, eles não terão acesso a "
"configurações importantes, como alterar o código privado ou excluir todo o "
"projeto."
msgid "Scan QR code" msgid "Scan QR code"
msgstr "Escanear o código QR" msgstr ""
msgid "Use a mobile device with a compatible app." msgid "Use a mobile device with a compatible app."
msgstr "Use um telefone celular ou tablet com um aplicativo compatível." msgstr ""
msgid "Send via Emails" msgid "Send via Emails"
msgstr "Enviar via E-mails" msgstr "Enviar via E-mails"
@ -951,11 +943,6 @@ msgid ""
"to the full project, including changing settings such as the private code" "to the full project, including changing settings such as the private code"
" or project email address, or even deleting the whole project." " or project email address, or even deleting the whole project."
msgstr "" msgstr ""
"Você pode compartilhar o identificador do projeto e o código privado por "
"qualquer meio de comunicação.<br />Qualquer pessoa com o código privado terá "
"acesso a todo o projeto, incluindo a alteração de configurações como o "
"código privado ou o endereço do e-mail, ou até mesmo a exclusão de todo o "
"projeto."
msgid "Identifier:" msgid "Identifier:"
msgstr "Identificador:" msgstr "Identificador:"
@ -965,7 +952,7 @@ msgid "Private code:"
msgstr "Código privado" msgstr "Código privado"
msgid "the private code was defined when you created the project" msgid "the private code was defined when you created the project"
msgstr "o código privado foi definido quando você criou o projeto" msgstr ""
msgid "Who pays?" msgid "Who pays?"
msgstr "Quem paga?" msgstr "Quem paga?"
@ -1153,3 +1140,4 @@ msgstr "Período"
#~ msgstr "" #~ msgstr ""
#~ "Pode compartilhar diretamente o seguinte " #~ "Pode compartilhar diretamente o seguinte "
#~ "ligação através do seu meio preferido" #~ "ligação através do seu meio preferido"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-05-10 07:26+0000\n" "PO-Revision-Date: 2023-05-05 00:47+0000\n"
"Last-Translator: Gesiane Pajarinen <gesianef@hotmail.com>\n" "Last-Translator: MurkBRA <anjo1077@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"i-hate-money/i-hate-money/pt_BR/>\n"
"Language: pt_BR\n" "Language: pt_BR\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/i"
"-hate-money/i-hate-money/pt_BR/>\n"
"Plural-Forms: nplurals=2; plural=n > 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.5.4-rc\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -23,23 +23,24 @@ msgid ""
"Not a valid amount or expression. Only numbers and + - * / operators are " "Not a valid amount or expression. Only numbers and + - * / operators are "
"accepted." "accepted."
msgstr "" msgstr ""
"Expressão ou quantia inválida. Apenas números e os operadores +=*/ são " "Expressão ou montante inválido. Apenas números e os operadores +=*/ são "
"aceitos." "aceitos."
msgid "Project name" msgid "Project name"
msgstr "Nome do projeto" msgstr "Nome do projeto"
#, fuzzy
msgid "Current private code" msgid "Current private code"
msgstr "Código privado atual" msgstr "Código privado novo"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "Insira o código privado para editar o projeto" msgstr ""
msgid "New private code" msgid "New private code"
msgstr "Novo código privado" msgstr "Código privado novo"
msgid "Enter a new code if you want to change it" msgid "Enter a new code if you want to change it"
msgstr "Digite um novo código se quiser mudá-lo" msgstr "Insira um novo código se quiser alterá-lo"
msgid "Email" msgid "Email"
msgstr "E-mail" msgstr "E-mail"
@ -66,11 +67,11 @@ msgid ""
"This project cannot be set to 'no currency' because it contains bills in " "This project cannot be set to 'no currency' because it contains bills in "
"multiple currencies." "multiple currencies."
msgstr "" msgstr ""
"Esse projeto não pode ser configurado como 'sem moeda' porque ele tem " "O projeto não pode ser definido como 'sem moeda' porque contém contas em "
"faturas em moedas diferentes." "várias moedas."
msgid "Compatible with Cospend" msgid "Compatible with Cospend"
msgstr "Compatível com Cospend" msgstr ""
msgid "Project identifier" msgid "Project identifier"
msgstr "Identificador do projeto" msgstr "Identificador do projeto"
@ -147,7 +148,7 @@ msgid "A link to an external document, related to this bill"
msgstr "Link para um documento externo, relacionado à essa conta" msgstr "Link para um documento externo, relacionado à essa conta"
msgid "For whom?" msgid "For whom?"
msgstr "Por quem?" msgstr "Para quem?"
msgid "Submit" msgid "Submit"
msgstr "Enviar" msgstr "Enviar"
@ -172,10 +173,10 @@ msgid "Add"
msgstr "Adicionar" msgstr "Adicionar"
msgid "The participant name is invalid" msgid "The participant name is invalid"
msgstr "O nome do participante não é válido" msgstr "'%(name)s' não é um usuário válido"
msgid "This project already have this participant" msgid "This project already have this participant"
msgstr "Este participante já está no projeto" msgstr "'%(name)s' já está no projeto"
msgid "People to notify" msgid "People to notify"
msgstr "Pessoas para notificar" msgstr "Pessoas para notificar"
@ -191,15 +192,16 @@ msgid "Logout"
msgstr "Sair" msgstr "Sair"
msgid "Please check the email configuration of the server." msgid "Please check the email configuration of the server."
msgstr "Verifique a configuração de e-mail do servidor." msgstr ""
#, python-format #, fuzzy, python-format
msgid "" msgid ""
"Please check the email configuration of the server or contact the " "Please check the email configuration of the server or contact the "
"administrator: %(admin_email)s" "administrator: %(admin_email)s"
msgstr "" msgstr ""
"Verifique a configuração de e-mail do servidor ou entre em contato com o " "Desculpe, houve um erro ao enviar os convites via e-mail. Por favor, "
"administrador: %(admin_email)s" "confira a configuração de email do servidor ou entre em contato com um "
"administrador."
#. List with two items only #. List with two items only
msgid "{dual_object_0} and {dual_object_1}" msgid "{dual_object_0} and {dual_object_1}"
@ -228,17 +230,18 @@ msgstr "{prefix}: {error}"
msgid "{prefix}:<br />{errors}" msgid "{prefix}:<br />{errors}"
msgstr "{prefix}:<br />{errors}" msgstr "{prefix}:<br />{errors}"
#, fuzzy
msgid "Too many failed login attempts." msgid "Too many failed login attempts."
msgstr "Muitas tentativas de login falhas." msgstr "Muitas tentativas de login falhas, por favor, tente novamente mais tarde."
#, python-format #, python-format
msgid "This admin password is not the right one. Only %(num)d attempts left." msgid "This admin password is not the right one. Only %(num)d attempts left."
msgstr "" msgstr ""
"Esta senha de administrador não está correta. Apenas %(num)d tentativas " "Esta senha de administrador não é a correta. Apenas %(num)d tentativas "
"restantes." "restantes."
msgid "Provided token is invalid" msgid "Provided token is invalid"
msgstr "O token fornecido é inválido" msgstr "Token invalido"
msgid "This private code is not the right one" msgid "This private code is not the right one"
msgstr "Este código privado não é o correto" msgstr "Este código privado não é o correto"
@ -250,15 +253,17 @@ msgid ""
"We tried to send you an reminder email, but there was an error. You can " "We tried to send you an reminder email, but there was an error. You can "
"still use the project normally." "still use the project normally."
msgstr "" msgstr ""
"Tentamos te enviar um email de lembrete, mas aconteceu um erro. Você pode " "Nós tentamos te enviar um email de lembrete, mas aconteceu um erro. Você "
"continuar usando o projeto normalmente." "pode continuar usando o projeto normalmente."
#, fuzzy
msgid "" msgid ""
"Sorry, there was an error while sending you an email with password reset " "Sorry, there was an error while sending you an email with password reset "
"instructions." "instructions."
msgstr "" msgstr ""
"Desculpe, houve um erro ao te enviar um email com as instruções de " "Desculpe, houve um erro ao te enviar um email com as instruções de "
"redefinição de senha." "redefinição de senha. Por favor, confira a configuração de e-mail do "
"servidor ou entre em contato com um administrador."
msgid "No token provided" msgid "No token provided"
msgstr "Nenhum token fornecido" msgstr "Nenhum token fornecido"
@ -270,36 +275,37 @@ msgid "Unknown project"
msgstr "Projeto desconhecido" msgstr "Projeto desconhecido"
msgid "Password successfully reset." msgid "Password successfully reset."
msgstr "Senha redefinida com sucesso." msgstr "Senha redefinida corretamente."
msgid "Project settings have been changed successfully." msgid "Project settings have been changed successfully."
msgstr "As configurações do projeto foram alteradas com sucesso." msgstr ""
msgid "Unable to parse CSV" msgid "Unable to parse CSV"
msgstr "Não foi possível processar o CSV" msgstr ""
#, python-format #, python-format
msgid "Missing attribute: %(attribute)s" msgid "Missing attribute: %(attribute)s"
msgstr "Faltando o atributo: %(attribute)s" msgstr ""
#, fuzzy
msgid "" msgid ""
"Cannot add bills in multiple currencies to a project without default " "Cannot add bills in multiple currencies to a project without default "
"currency" "currency"
msgstr "" msgstr ""
"Não é possível adicionar contas em várias moedas em um projeto sem moeda " "Não é possível adicionar contas em várias moedas a um projeto sem moeda "
"padrão" "padrão"
msgid "Project successfully uploaded" msgid "Project successfully uploaded"
msgstr "Projeto enviado com sucesso" msgstr "Projeto enviado corretamente"
msgid "Project successfully deleted" msgid "Project successfully deleted"
msgstr "Projeto deletado com sucesso" msgstr "Projeto deletado com sucesso"
msgid "Error deleting project" msgid "Error deleting project"
msgstr "Erro ao deletar o projeto" msgstr "Erro ao excluir o projeto"
msgid "Unable to logout" msgid "Unable to logout"
msgstr "Não é possível sair" msgstr ""
#, python-format #, python-format
msgid "You have been invited to share your expenses for %(project)s" msgid "You have been invited to share your expenses for %(project)s"
@ -308,73 +314,76 @@ msgstr "Você foi convidado a compartilhar suas despesas com %(project)s"
msgid "Your invitations have been sent" msgid "Your invitations have been sent"
msgstr "Seus convites foram enviados" msgstr "Seus convites foram enviados"
#, fuzzy
msgid "Sorry, there was an error while trying to send the invitation emails." msgid "Sorry, there was an error while trying to send the invitation emails."
msgstr "Desculpe, houve um erro ao enviar os convites via e-mail." msgstr ""
"Desculpe, houve um erro ao enviar os convites via e-mail. Por favor, "
"confira a configuração de email do servidor ou entre em contato com um "
"administrador."
#, python-format #, python-format
msgid "%(member)s has been added" msgid "%(member)s has been added"
msgstr "%(member)s foi adicionado(a)" msgstr "%(member)s foram adicionados"
msgid "Error activating participant" msgid "Error activating participant"
msgstr "Erro ao ativar participante" msgstr "Erro ao ativar usuário"
#, python-format #, python-format
msgid "%(name)s is part of this project again" msgid "%(name)s is part of this project again"
msgstr "%(name)s faz parte deste projeto novamente" msgstr "%(name)s faz parte deste projeto novamente"
msgid "Error removing participant" msgid "Error removing participant"
msgstr "Erro ao remover participante" msgstr "Erro ao remover usuário"
#, python-format #, python-format
msgid "" msgid ""
"Participant '%(name)s' has been deactivated. It will still appear in the " "Participant '%(name)s' has been deactivated. It will still appear in the "
"list until its balance reach zero." "list until its balance reach zero."
msgstr "" msgstr ""
"O participante '%(name)s' foi desativado. Ele continuará aparecendo na lista " "Usuário '%(name)s' foi desativado. Ele continuará aparecendo na lista de "
"de usuários até que seu saldo seja zero." "usuários até que seu saldo seja zero."
#, python-format #, fuzzy, python-format
msgid "Participant '%(name)s' has been removed" msgid "Participant '%(name)s' has been removed"
msgstr "Participante '%(name)s' foi removido(a)" msgstr "Usuário '%(name)s' foi removido"
#, python-format #, fuzzy, python-format
msgid "Participant '%(name)s' has been modified" msgid "Participant '%(name)s' has been modified"
msgstr "Participante '%(name)s' foi alterado(a)" msgstr "Usuário '%(name)s' foi removido"
msgid "The bill has been added" msgid "The bill has been added"
msgstr "A conta foi adicionada" msgstr "A conta foi adicionada"
msgid "Error deleting bill" msgid "Error deleting bill"
msgstr "Erro ao excluir a conta" msgstr "Erro ao excluir conta"
msgid "The bill has been deleted" msgid "The bill has been deleted"
msgstr "A conta foi deletada" msgstr "A conta foi deletada"
msgid "The bill has been modified" msgid "The bill has been modified"
msgstr "A conta foi alterada" msgstr "A conta foi modificada"
#, python-format #, python-format
msgid "%(lang)s is not a supported language" msgid "%(lang)s is not a supported language"
msgstr "Não temos configuração para o idioma %(lang)s" msgstr ""
msgid "Error deleting project history" msgid "Error deleting project history"
msgstr "Erro ao deletar o histórico do projeto" msgstr "Erro ao deletar o histórico do projeto"
msgid "Deleted project history." msgid "Deleted project history."
msgstr "Histórico do projeto deletado." msgstr "Histórico do projeto apagado."
msgid "Error deleting recorded IP addresses" msgid "Error deleting recorded IP addresses"
msgstr "Erro ao deletar endereços IP salvos" msgstr "Erro ao excluir endereços IP salvos"
msgid "Deleted recorded IP addresses in project history." msgid "Deleted recorded IP addresses in project history."
msgstr "Endereços de IP registrados deletados no histórico do projeto." msgstr "Endereços IP salvos no histórico de projeto deletados."
msgid "Sorry, we were unable to find the page you've asked for." msgid "Sorry, we were unable to find the page you've asked for."
msgstr "Desculpe, não foi possível encontrar a página que você solicitou." msgstr "Desculpe, não foi possível encontrar a página que você solicitou."
msgid "The best thing to do is probably to get back to the main page." msgid "The best thing to do is probably to get back to the main page."
msgstr "" msgstr "É provável que a melhor coisa a fazer seja voltar para a página inicial."
"Provavelmente, a melhor coisa a fazer seja voltar para a página inicial."
msgid "Back to the list" msgid "Back to the list"
msgstr "Voltar para a lista" msgstr "Voltar para a lista"
@ -389,7 +398,7 @@ msgid "The project you are trying to access do not exist, do you want to"
msgstr "O projeto que você está tentando acessar não existe, você quer" msgstr "O projeto que você está tentando acessar não existe, você quer"
msgid "create it" msgid "create it"
msgstr "criar" msgstr "Criar"
msgid "?" msgid "?"
msgstr "?" msgstr "?"
@ -401,7 +410,7 @@ msgid "Project"
msgstr "Projeto" msgstr "Projeto"
msgid "Number of participants" msgid "Number of participants"
msgstr "Número de participantes" msgstr "Número de usuários"
msgid "Number of bills" msgid "Number of bills"
msgstr "Número de contas" msgstr "Número de contas"
@ -419,28 +428,29 @@ msgid "edit"
msgstr "editar" msgstr "editar"
msgid "Delete project" msgid "Delete project"
msgstr "Deletar projeto" msgstr "Excluir projeto"
msgid "show" msgid "show"
msgstr "mostrar" msgstr "exibir"
msgid "The Dashboard is currently deactivated." msgid "The Dashboard is currently deactivated."
msgstr "O Painel de Controle atualmente está desativado." msgstr "O Painel de Controle atualmente está desativado."
msgid "Download Mobile Application" msgid "Download Mobile Application"
msgstr "Baixar o aplicativo" msgstr "Baixar o APP"
msgid "Get it on" msgid "Get it on"
msgstr "Comece" msgstr "Pegue agora"
msgid "Edit project" msgid "Edit project"
msgstr "Editar projeto" msgstr "Editar projeto"
#, fuzzy
msgid "Import project" msgid "Import project"
msgstr "Importar projeto" msgstr "Editar projeto"
msgid "Download project's data" msgid "Download project's data"
msgstr "Baixar os dados do projeto" msgstr "Baixar dados do projeto"
msgid "Bill items" msgid "Bill items"
msgstr "Itens da conta" msgstr "Itens da conta"
@ -464,13 +474,14 @@ msgid "Privacy Settings"
msgstr "Configurações de Privacidade" msgstr "Configurações de Privacidade"
msgid "Save changes" msgid "Save changes"
msgstr "Salvar as alterações" msgstr ""
msgid "This will remove all bills and participants in this project!" msgid "This will remove all bills and participants in this project!"
msgstr "Isso vai remover todas as faturas e participantes desse projeto!" msgstr "Isso vai remover todas as contas e participantes desse projeto!"
#, fuzzy
msgid "Import previously exported project" msgid "Import previously exported project"
msgstr "Importar projeto que foi exportado anteriormente" msgstr "Importar arquivo JSON exportado anteriormente"
msgid "Choose file" msgid "Choose file"
msgstr "Escolher arquivo" msgstr "Escolher arquivo"
@ -482,7 +493,7 @@ msgid "Add a bill"
msgstr "Adicionar uma conta" msgstr "Adicionar uma conta"
msgid "Simple operations are allowed, e.g. (18+36.2)/3" msgid "Simple operations are allowed, e.g. (18+36.2)/3"
msgstr "Operações simples são permitidas, por ex. (18+36,2)/3" msgstr ""
msgid "Everyone" msgid "Everyone"
msgstr "Todos" msgstr "Todos"
@ -497,7 +508,7 @@ msgid "Add participant"
msgstr "Adicionar participante" msgstr "Adicionar participante"
msgid "Edit this participant" msgid "Edit this participant"
msgstr "Editar participante" msgstr "Editar usuário"
msgid "john.doe@example.com, mary.moe@site.com" msgid "john.doe@example.com, mary.moe@site.com"
msgstr "john.doe@example.com, mary.moe@site.com" msgstr "john.doe@example.com, mary.moe@site.com"
@ -506,22 +517,22 @@ msgid "Download"
msgstr "Baixar" msgstr "Baixar"
msgid "Disabled Project History" msgid "Disabled Project History"
msgstr "Histórico de Projeto Desativado" msgstr "Histórico do Projeto Desativado"
msgid "Disabled Project History & IP Address Recording" msgid "Disabled Project History & IP Address Recording"
msgstr "Histórico de Projeto Desativado & Registro de Endereços de IP" msgstr "Histórico do Projeto Desativado & Gravação de Endereço IP"
msgid "Enabled Project History" msgid "Enabled Project History"
msgstr "Histórico de Projeto Ativado" msgstr "Histórico do Projeto Ativado"
msgid "Disabled IP Address Recording" msgid "Disabled IP Address Recording"
msgstr "Registro de Endereços de IP Desativados" msgstr "Gravação do Endereço IP Desativada"
msgid "Enabled Project History & IP Address Recording" msgid "Enabled Project History & IP Address Recording"
msgstr "Histórico de Projeto Ativado & Registro de Endereços de IP" msgstr "Histórico do Projeto Ativado & Gravação do Endereço IP"
msgid "Enabled IP Address Recording" msgid "Enabled IP Address Recording"
msgstr "Registro de Endereços de IP Ativado" msgstr "Gravação do Endereço IP Ativada"
msgid "History Settings Changed" msgid "History Settings Changed"
msgstr "Configurações do Histórico Alteradas" msgstr "Configurações do Histórico Alteradas"
@ -543,13 +554,13 @@ msgid ""
" The rest of the project history will be unaffected. This " " The rest of the project history will be unaffected. This "
"action cannot be undone." "action cannot be undone."
msgstr "" msgstr ""
"Tem certeza que deseja deletar todos os endereços IP gravados neste projeto?" "Você tem certeza que deseja deletar todos os endereços IP gravados deste "
"\n" "projeto?\n"
" O resto do histórico do projeto não será afetado. Esta ação " " O resto do histórico do projeto não será afetado. Esta "
"não pode ser desfeita." "ação não pode ser desfeita."
msgid "Confirm deletion" msgid "Confirm deletion"
msgstr "Confirmar a exclusão" msgstr "Confirmar exclusão"
msgid "Close" msgid "Close"
msgstr "Fechar" msgstr "Fechar"
@ -566,28 +577,27 @@ msgstr ""
#, python-format #, python-format
msgid "Bill %(name)s: added %(owers_list_str)s to owers list" msgid "Bill %(name)s: added %(owers_list_str)s to owers list"
msgstr "Conta %(name)s: adicionados %(owers_list_str)s à lista de proprietários" msgstr "Conta %(name)s: adicionou %(owers_list_str)s a lista de devedores"
#, python-format #, python-format
msgid "Bill %(name)s: removed %(owers_list_str)s from owers list" msgid "Bill %(name)s: removed %(owers_list_str)s from owers list"
msgstr "Conta %(name)s: removidos %(owers_list_str)s da lista de proprietários" msgstr "Conta %(name)s: removeu %(owers_list_str)s da lista de devedores"
msgid "This project has history disabled. New actions won't appear below." msgid "This project has history disabled. New actions won't appear below."
msgstr "" msgstr ""
"Este projeto tem o histórico desativado. Novas ações não aparecerão abaixo."
#, fuzzy
msgid "You can enable history on the settings page." msgid "You can enable history on the settings page."
msgstr "Você pode ativar o histórico na página de configurações." msgstr "A gravação do endereço IP pode ser ativada na página de configurações"
msgid "" msgid ""
"The table below reflects actions recorded prior to disabling project " "The table below reflects actions recorded prior to disabling project "
"history." "history."
msgstr "" msgstr ""
"A tabela abaixo contém as ações registradas antes da desativação do "
"histórico do projeto."
#, fuzzy
msgid "You can clear the project history to remove them." msgid "You can clear the project history to remove them."
msgstr "Você pode limpar o histórico do projeto para removê-los." msgstr "Alguém provavelmente limpou o histórico do projeto."
msgid "" msgid ""
"Some entries below contain IP addresses, even though this project has IP " "Some entries below contain IP addresses, even though this project has IP "
@ -603,7 +613,7 @@ msgid "No IP Addresses to erase"
msgstr "Não há endereços IP para apagar" msgstr "Não há endereços IP para apagar"
msgid "Delete Stored IP Addresses" msgid "Delete Stored IP Addresses"
msgstr "Deletar Endereços IP Salvos" msgstr "Deletar endereços IP salvos"
msgid "No history to erase" msgid "No history to erase"
msgstr "Não há histórico para apagar" msgstr "Não há histórico para apagar"
@ -612,7 +622,7 @@ msgid "Clear Project History"
msgstr "Limpar Histórico do Projeto" msgstr "Limpar Histórico do Projeto"
msgid "Time" msgid "Time"
msgstr "Data e hora" msgstr "Tempo"
msgid "Event" msgid "Event"
msgstr "Evento" msgstr "Evento"
@ -624,7 +634,7 @@ msgid "IP address recording can be disabled on the settings page"
msgstr "A gravação do endereço IP pode ser desativada na página de configurações" msgstr "A gravação do endereço IP pode ser desativada na página de configurações"
msgid "From IP" msgid "From IP"
msgstr "IP" msgstr "Do IP"
#, python-format #, python-format
msgid "Project %(name)s added" msgid "Project %(name)s added"
@ -636,7 +646,7 @@ msgstr "Conta %(name)s adicionada"
#, python-format #, python-format
msgid "Participant %(name)s added" msgid "Participant %(name)s added"
msgstr "Participante %(name)s adicionado(a)" msgstr "Usuário %(name)s adicionado"
msgid "Project private code changed" msgid "Project private code changed"
msgstr "Código privado do projeto alterado" msgstr "Código privado do projeto alterado"
@ -654,15 +664,15 @@ msgstr "Configurações do projeto alteradas"
#, python-format #, python-format
msgid "Participant %(name)s deactivated" msgid "Participant %(name)s deactivated"
msgstr "Participante %(name)s desativado" msgstr "Usuário %(name)s desativado"
#, python-format #, python-format
msgid "Participant %(name)s reactivated" msgid "Participant %(name)s reactivated"
msgstr "Participante %(name)s reativado" msgstr "Usuário %(name)s reativado"
#, python-format #, python-format
msgid "Participant %(name)s renamed to %(new_name)s" msgid "Participant %(name)s renamed to %(new_name)s"
msgstr "Participante %(name)s renomeado para %(new_name)s" msgstr "Usuário %(name)s renomeado para %(new_name)s"
#, python-format #, python-format
msgid "Bill %(name)s renamed to %(new_description)s" msgid "Bill %(name)s renamed to %(new_description)s"
@ -670,8 +680,7 @@ msgstr "Conta %(name)s renomeada para %(new_description)s"
#, python-format #, python-format
msgid "Participant %(name)s: weight changed from %(old_weight)s to %(new_weight)s" msgid "Participant %(name)s: weight changed from %(old_weight)s to %(new_weight)s"
msgstr "" msgstr "Usuário %(name)s: a carga mudou de %(old_weight)s para %(new_weight)s"
"Participante %(name)s: o peso mudou de %(old_weight)s para %(new_weight)s"
msgid "Payer" msgid "Payer"
msgstr "Pagador" msgstr "Pagador"
@ -688,40 +697,40 @@ msgstr "Quantia em %(currency)s"
#, python-format #, python-format
msgid "Bill %(name)s modified" msgid "Bill %(name)s modified"
msgstr "Conta %(name)s alterada" msgstr "Conta %(name)s modificada"
#, python-format #, python-format
msgid "Participant %(name)s modified" msgid "Participant %(name)s modified"
msgstr "Participante %(name)s foi alterado(a)" msgstr "Usuário %(name)s modificado"
#, python-format #, python-format
msgid "Bill %(name)s removed" msgid "Bill %(name)s removed"
msgstr "Conta '%(name)s' foi removida" msgstr "Conta '%(name)s' foi removida"
#, python-format #, fuzzy, python-format
msgid "Participant %(name)s removed" msgid "Participant %(name)s removed"
msgstr "Participante '%(name)s' foi removido" msgstr "Usuário '%(name)s' foi removido"
#, python-format #, python-format
msgid "Project %(name)s changed in an unknown way" msgid "Project %(name)s changed in an unknown way"
msgstr "Projeto %(name)s alterado de maneira desconhecida" msgstr "Projeto %(name)s modificado de maneira desconhecida"
#, python-format #, python-format
msgid "Bill %(name)s changed in an unknown way" msgid "Bill %(name)s changed in an unknown way"
msgstr "Conta %(name)s alterada de maneira desconhecida" msgstr "Conta %(name)s modificado de maneira desconhecida"
#, python-format #, python-format
msgid "Participant %(name)s changed in an unknown way" msgid "Participant %(name)s changed in an unknown way"
msgstr "Participante %(name)s alterado(a) de maneira desconhecida" msgstr "Participante %(name)s modificado de maneira desconhecida"
msgid "Nothing to list" msgid "Nothing to list"
msgstr "Nada para listar" msgstr "Nada a listar"
msgid "Someone probably cleared the project history." msgid "Someone probably cleared the project history."
msgstr "Alguém provavelmente limpou o histórico do projeto." msgstr "Alguém provavelmente limpou o histórico do projeto."
msgid "Manage your shared <br />expenses, easily" msgid "Manage your shared <br />expenses, easily"
msgstr "Gerencie suas despesas <br /> compartilhadas com facilidade" msgstr "Modifique suas despesas <br />compartilhadas, facilmente"
msgid "Try out the demo" msgid "Try out the demo"
msgstr "Experimente a demonstração" msgstr "Experimente a demonstração"
@ -739,13 +748,13 @@ msgid "We can help!"
msgstr "Nós podemos ajudar!" msgstr "Nós podemos ajudar!"
msgid "Log in to an existing project" msgid "Log in to an existing project"
msgstr "Faça login em um projeto existente" msgstr "Conecte-se em um projeto existente"
msgid "Log in" msgid "Log in"
msgstr "Login" msgstr "Conecte-se"
msgid "can't remember your password?" msgid "can't remember your password?"
msgstr "não lembra sua senha?" msgstr "não consegue se lembrar da sua senha?"
msgid "Create" msgid "Create"
msgstr "Criar" msgstr "Criar"
@ -764,13 +773,13 @@ msgid "Bills"
msgstr "Contas" msgstr "Contas"
msgid "Settle" msgid "Settle"
msgstr "Pagamentos" msgstr "Estabelecer"
msgid "Statistics" msgid "Statistics"
msgstr "Estatísticas" msgstr "Estatísticas"
msgid "Languages" msgid "Languages"
msgstr "Idiomas" msgstr "Linguagens"
msgid "Projects" msgid "Projects"
msgstr "Projetos" msgstr "Projetos"
@ -785,7 +794,7 @@ msgid "Settings"
msgstr "Configurações" msgstr "Configurações"
msgid "RSS Feed" msgid "RSS Feed"
msgstr "Feed RSS" msgstr ""
msgid "Other projects :" msgid "Other projects :"
msgstr "Outros projetos:" msgstr "Outros projetos:"
@ -798,18 +807,18 @@ msgstr "Painel de controle"
#, python-format #, python-format
msgid "Please retry after %(date)s." msgid "Please retry after %(date)s."
msgstr "Por favor, tente novamente após %(date)s." msgstr ""
msgid "Code" msgid "Code"
msgstr "Código" msgstr "Código"
msgid "Mobile Application" msgid "Mobile Application"
msgstr "Aplicativo" msgstr "Aplicação Mobile"
msgid "Documentation" msgid "Documentation"
msgstr "Documentação" msgstr "Documentação"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Painel de Administração" msgstr "Painel de Administração"
msgid "Legal information" msgid "Legal information"
@ -838,7 +847,7 @@ msgid "Older bills"
msgstr "Contas mais antigas" msgstr "Contas mais antigas"
msgid "You should start by adding participants" msgid "You should start by adding participants"
msgstr "Comece adicionando participantes" msgstr "Você deveria começar adicionando pessoas"
msgid "Add a new bill" msgid "Add a new bill"
msgstr "Adicionar uma nova conta" msgstr "Adicionar uma nova conta"
@ -863,11 +872,13 @@ msgstr "Sem contas"
msgid "Nothing to list yet." msgid "Nothing to list yet."
msgstr "Nada para listar ainda." msgstr "Nada para listar ainda."
#, fuzzy
msgid "Add your first bill" msgid "Add your first bill"
msgstr "Adicione sua primeira conta" msgstr "adicionar uma conta"
#, fuzzy
msgid "Add the first participant" msgid "Add the first participant"
msgstr "Adicione o primeiro participante" msgstr "Editar usuário"
msgid "Password reminder" msgid "Password reminder"
msgstr "Lembrete de senha" msgstr "Lembrete de senha"
@ -892,7 +903,7 @@ msgid "Invite people to join this project"
msgstr "Convide pessoas para participar deste projeto" msgstr "Convide pessoas para participar deste projeto"
msgid "Share an invitation link" msgid "Share an invitation link"
msgstr "Compartilhe um link do convite" msgstr ""
msgid "" msgid ""
"The easiest way to invite people is to give them the following invitation" "The easiest way to invite people is to give them the following invitation"
@ -900,29 +911,26 @@ msgid ""
" add/edit/delete bills. However, they will not have access to important " " add/edit/delete bills. However, they will not have access to important "
"settings such as changing the private code or deleting the whole project." "settings such as changing the private code or deleting the whole project."
msgstr "" msgstr ""
"A maneira mais fácil de convidar as pessoas é enviando o seguinte link para "
"convite.<br />Eles poderão acessar o projeto, gerenciar participantes, "
"adicionar/editar/excluir contas. No entanto, eles não terão acesso às "
"configurações importantes, como alterar o código privado ou excluir todo o "
"projeto."
msgid "Scan QR code" msgid "Scan QR code"
msgstr "Escaneie o QR code" msgstr ""
msgid "Use a mobile device with a compatible app." msgid "Use a mobile device with a compatible app."
msgstr "Use um telefone celular ou tablet com um aplicativo compatível." msgstr ""
msgid "Send via Emails" msgid "Send via Emails"
msgstr "Enviar via E-mails" msgstr "Enviar via E-mails"
#, fuzzy
msgid "" msgid ""
"Specify a list of email adresses (separated by comma) of people you want " "Specify a list of email adresses (separated by comma) of people you want "
"to notify about the creation of this project. We will send them an email " "to notify about the creation of this project. We will send them an email "
"with the invitation link." "with the invitation link."
msgstr "" msgstr ""
"Especifique uma lista de e-mails (separados por vírgula) das pessoas que " "Especifica uma lista de endereços de email (separados por vírgula) que "
"você deseja notificar sobre a criação deste projeto. Enviaremos um e-mail " "você quer notificar acerca da\n"
"para eles com o link do convite." " criação deste projeto de gestão de saldo e nós iremos "
"enviar um email por si."
msgid "Share Identifier & code" msgid "Share Identifier & code"
msgstr "Compartilhar Identificador & código" msgstr "Compartilhar Identificador & código"
@ -933,19 +941,16 @@ msgid ""
"to the full project, including changing settings such as the private code" "to the full project, including changing settings such as the private code"
" or project email address, or even deleting the whole project." " or project email address, or even deleting the whole project."
msgstr "" msgstr ""
"Você pode compartilhar o identificador do projeto e o código privado por "
"qualquer meio de comunicação.<br />Qualquer pessoa com o código privado terá "
"acesso a todo o projeto e poderá alterar as configurações como o código "
"privado ou o e-mail, ou até mesmo a exclusão de todo o projeto."
msgid "Identifier:" msgid "Identifier:"
msgstr "Identificador:" msgstr "Identificador:"
#, fuzzy
msgid "Private code:" msgid "Private code:"
msgstr "Código privado:" msgstr "Código privado"
msgid "the private code was defined when you created the project" msgid "the private code was defined when you created the project"
msgstr "o código privado foi definido quando você criou o projeto" msgstr ""
msgid "Who pays?" msgid "Who pays?"
msgstr "Quem paga?" msgstr "Quem paga?"
@ -1135,3 +1140,4 @@ msgstr "Período"
#~ "Você pode compartilhar diretamente o " #~ "Você pode compartilhar diretamente o "
#~ "seguinte link através do seu meio " #~ "seguinte link através do seu meio "
#~ "preferido" #~ "preferido"

Binary file not shown.

View file

@ -816,7 +816,7 @@ msgstr "Мобильное приложение"
msgid "Documentation" msgid "Documentation"
msgstr "Документация" msgstr "Документация"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Панель инструментов администратора" msgstr "Панель инструментов администратора"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -783,7 +783,7 @@ msgstr "Mobilna Aplikacija"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentacija" msgstr "Dokumentacija"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -3,8 +3,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2023-11-20 05:10+0000\n" "PO-Revision-Date: 2023-09-15 00:53+0000\n"
"Last-Translator: Jesper <93771679+Bjorkan@users.noreply.github.com>\n" "Last-Translator: Kristoffer Grundström <swedishsailfishosuser@tutanota.com>\n"
"Language-Team: Swedish <https://hosted.weblate.org/projects/i-hate-money/" "Language-Team: Swedish <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/sv/>\n" "i-hate-money/sv/>\n"
"Language: sv\n" "Language: sv\n"
@ -12,17 +12,19 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.2\n" "X-Generator: Weblate 5.0.1-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
msgid "You have just created '%(project)s' to share your expenses" msgid "You have just created '%(project)s' to share your expenses"
msgstr "Du har just skapat '%(project)s för att dela ut dina kostnader" msgstr "Du har just skapat '%(project)s' för att dela ut dina kostnader"
msgid "" msgid ""
"Not a valid amount or expression. Only numbers and + - * / operators are " "Not a valid amount or expression. Only numbers and + - * / operators are "
"accepted." "accepted."
msgstr "Ogiltigt värde eller uttryck. Endast nummer och +-*/ är tillåtna." msgstr ""
"Inte en giltig summa eller uttryck. Endast nummer och +-* /-operatörer "
"accepteras."
msgid "Project name" msgid "Project name"
msgstr "Namn på projektet" msgstr "Namn på projektet"
@ -43,7 +45,7 @@ msgid "Email"
msgstr "E-post" msgstr "E-post"
msgid "Enable project history" msgid "Enable project history"
msgstr "Aktivera projekthistorik" msgstr "Aktivera historik för projektet"
msgid "Use IP tracking for project history" msgid "Use IP tracking for project history"
msgstr "Använd IP-spårning för projekthistorik" msgstr "Använd IP-spårning för projekthistorik"
@ -175,7 +177,7 @@ msgid "The participant name is invalid"
msgstr "Deltagarens namn är ogiltigt" msgstr "Deltagarens namn är ogiltigt"
msgid "This project already have this participant" msgid "This project already have this participant"
msgstr "Den här deltagaren är redan i projektet" msgstr "Den här deltagaren är redan i projektet."
msgid "People to notify" msgid "People to notify"
msgstr "Personer att underrätta" msgstr "Personer att underrätta"
@ -818,7 +820,7 @@ msgstr "Mobilapplikation"
msgid "Documentation" msgid "Documentation"
msgstr "Dokumentation" msgstr "Dokumentation"
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "Översiktspanel för administration" msgstr "Översiktspanel för administration"
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -809,7 +809,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"

Binary file not shown.

View file

@ -1,18 +1,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-29 14:24+0200\n" "POT-Creation-Date: 2023-07-29 14:24+0200\n"
"PO-Revision-Date: 2024-05-13 07:00+0000\n" "PO-Revision-Date: 2022-10-04 14:19+0000\n"
"Last-Translator: Harshini K <harshinikondepudi@gmail.com>\n" "Last-Translator: Sharan J <sharanjs1999@gmail.com>\n"
"Language-Team: Telugu <https://hosted.weblate.org/projects/i-hate-money/"
"i-hate-money/te/>\n"
"Language: te\n" "Language: te\n"
"Language-Team: Telugu <https://hosted.weblate.org/projects/i-hate-money/i"
"-hate-money/te/>\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.5.5-dev\n"
"Generated-By: Babel 2.9.0\n" "Generated-By: Babel 2.9.0\n"
#, python-format #, python-format
@ -35,7 +35,7 @@ msgid "Current private code"
msgstr "కొత్త ప్రైవేట్ కోడ్" msgstr "కొత్త ప్రైవేట్ కోడ్"
msgid "Enter existing private code to edit project" msgid "Enter existing private code to edit project"
msgstr "ప్రాజెక్ట్‌ను సవరించడానికి ఇప్పటికీ ఉన్న ప్రైవేట్ కోడ్ ని ఎంటర్ చేయండి" msgstr ""
msgid "New private code" msgid "New private code"
msgstr "కొత్త ప్రైవేట్ కోడ్" msgstr "కొత్త ప్రైవేట్ కోడ్"
@ -75,7 +75,7 @@ msgstr ""
" చెయ్యడం కుదరదు." " చెయ్యడం కుదరదు."
msgid "Compatible with Cospend" msgid "Compatible with Cospend"
msgstr "కాస్పెండ్ తో అనుకూలమైనది" msgstr ""
#, fuzzy #, fuzzy
msgid "Project identifier" msgid "Project identifier"
@ -136,16 +136,16 @@ msgid "Reset password"
msgstr "పాస్ వర్డ్ రీసెట్ చేయి" msgstr "పాస్ వర్డ్ రీసెట్ చేయి"
msgid "When?" msgid "When?"
msgstr "ఎప్పుడు?" msgstr ""
msgid "What?" msgid "What?"
msgstr "ఏమిటి?" msgstr "ఏమిటి?"
msgid "Who paid?" msgid "Who paid?"
msgstr "ఎవరు చెల్లించారు?" msgstr ""
msgid "How much?" msgid "How much?"
msgstr "ఎంత?" msgstr ""
msgid "Currency" msgid "Currency"
msgstr "కరెన్సీ" msgstr "కరెన్సీ"
@ -817,7 +817,7 @@ msgstr ""
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
msgid "Administration Dashboard" msgid "Administation Dashboard"
msgstr "" msgstr ""
msgid "Legal information" msgid "Legal information"
@ -1063,3 +1063,4 @@ msgstr ""
#~ "them an email with the invitation " #~ "them an email with the invitation "
#~ "link." #~ "link."
#~ msgstr "" #~ msgstr ""

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more