Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3b151e150 | ||
|
|
19c009f6b8 | ||
|
|
3b83a5f442 | ||
|
|
6b34b187f1 | ||
|
|
547adafcda | ||
|
|
8cc5689724 | ||
|
|
fc0feee736 | ||
|
|
548a8dc5ee | ||
|
|
157ed4fd96 | ||
|
|
0f4c96fc46 | ||
|
|
858114657c | ||
|
|
fca040a44d | ||
|
|
b2f9498142 | ||
|
|
ed97deadd3 | ||
|
|
8875b98980 | ||
|
|
eb78848601 | ||
|
|
a9f008683f | ||
|
|
52a2b552cb | ||
|
|
0e77a666f4 | ||
|
|
c49d0ea220 | ||
|
|
05a793ee39 | ||
|
|
763c8c42e5 | ||
|
|
5fee0440c2 | ||
|
|
da8473406e | ||
|
|
39d55d908a | ||
|
|
409784672c | ||
|
|
d27cbdba47 | ||
|
|
048ac4da0a | ||
|
|
a86e92e414 | ||
|
|
76c58a7f61 | ||
|
|
a59352d5da | ||
|
|
ef2062071a | ||
|
|
86a20d6b23 | ||
|
|
a21f0646b5 | ||
|
|
4c1a6b9e55 | ||
|
|
ff42f0ab66 | ||
|
|
6ea6cfac3e | ||
|
|
e3f70d0635 | ||
|
|
5bf31e5b99 | ||
|
|
ed321f9880 | ||
|
|
070f6623b7 | ||
|
|
c556c18dc5 | ||
|
|
65c9e01ad3 | ||
|
|
4bcc9291a4 | ||
|
|
5fd3204990 | ||
|
|
436aff00d2 | ||
|
|
2710afd560 | ||
|
|
4b69306f50 | ||
|
|
d6550e34c1 | ||
|
|
de0cbb75ff | ||
|
|
a33b578027 | ||
|
|
de930cc9ad | ||
|
|
a8ea05eae8 | ||
|
|
6c995ebd54 | ||
|
|
069554836b | ||
|
|
c5726670e7 | ||
|
|
81c42e5b8b | ||
|
|
feec11f99c | ||
|
|
d641540b65 | ||
|
|
2814811aea | ||
|
|
af4bfe3780 | ||
|
|
a11efc79c1 | ||
|
|
e63f3aa68f | ||
|
|
d77411c21e | ||
|
|
94c101cf7b | ||
|
|
2bced00f82 | ||
|
|
233b338bc5 | ||
|
|
728e072376 | ||
|
|
9fec8f9eaa | ||
|
|
6346fc8ec5 | ||
|
|
1c83ebd6f9 | ||
|
|
a65c3c9dfe | ||
|
|
86c084da6f | ||
|
|
03712f1503 | ||
|
|
ffbcb6b74d | ||
|
|
0a16a4ad38 | ||
|
|
75747157f0 | ||
|
|
2c4b4f1594 | ||
|
|
2fda3e453c | ||
|
|
c14c854a79 | ||
|
|
0c3368fd35 | ||
|
|
92909ce27f | ||
|
|
ff6c48a0c8 | ||
|
|
6c5c9d5bed | ||
|
|
f9307fd22d | ||
|
|
9302a32f4c | ||
|
|
98e2345bb9 | ||
|
|
5732f78e80 | ||
|
|
72ad0a4c90 | ||
|
|
2c973f976f | ||
|
|
5374d9e9c7 | ||
|
|
5111f3574f | ||
|
|
4db788680e | ||
|
|
39c1a2ffc6 | ||
|
|
f5154393e2 | ||
|
|
e9d583113a | ||
|
|
21d0c02687 | ||
|
|
2281316d58 | ||
|
|
210c12b7ef | ||
|
|
66e15e419e | ||
|
|
727803ea5c | ||
|
|
7add7efea2 | ||
|
|
a7c80f65c3 | ||
|
|
1e4edf7504 | ||
|
|
24053ca5ab | ||
|
|
343363d54f | ||
|
|
8742bd59da | ||
|
|
8eea062218 | ||
|
|
9a5674e239 | ||
|
|
50b3a2e431 | ||
|
|
e8d46cd4f3 | ||
|
|
8f896f7412 | ||
|
|
504631454a | ||
|
|
345f3716c9 | ||
|
|
5fff8da08d | ||
|
|
07e24f7fcb | ||
|
|
5dfe03b3f1 | ||
|
|
26bed11116 | ||
|
|
972bb9dadb | ||
|
|
4f5e124ff0 | ||
|
|
c392c06b39 | ||
|
|
002e867bc4 | ||
|
|
9b8f716a6a | ||
|
|
853f1791d2 | ||
|
|
7145cb6f30 | ||
|
|
e990e00a75 |
@@ -1,2 +1,4 @@
|
||||
POSTGRES_PRISMA_URL=postgresql://postgres:1234@localhost
|
||||
POSTGRES_URL_NON_POOLING=postgresql://postgres:1234@localhost
|
||||
POSTGRES_URL_NON_POOLING=postgresql://postgres:1234@localhost
|
||||
|
||||
NEXT_PUBLIC_DEFAULT_CURRENCY_CODE=""
|
||||
46
.github/workflows/cd.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Create and publish a Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Downcase repo
|
||||
run: |
|
||||
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ env.REPO }}:${{ github.ref_name }}
|
||||
ghcr.io/${{ env.REPO }}:latest
|
||||
provenance: false # Disable provenance to avoid unknown/unknown
|
||||
sbom: false # Disable sbom to avoid unknown/unknown
|
||||
13
Dockerfile
@@ -1,9 +1,9 @@
|
||||
FROM node:21-alpine as base
|
||||
FROM node:21-alpine AS base
|
||||
|
||||
WORKDIR /usr/app
|
||||
COPY ./package.json \
|
||||
./package-lock.json \
|
||||
./next.config.js \
|
||||
./next.config.mjs \
|
||||
./tsconfig.json \
|
||||
./reset.d.ts \
|
||||
./tailwind.config.js \
|
||||
@@ -16,6 +16,7 @@ RUN apk add --no-cache openssl && \
|
||||
npx prisma generate
|
||||
|
||||
COPY ./src ./src
|
||||
COPY ./messages ./messages
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
@@ -24,21 +25,21 @@ RUN npm run build
|
||||
|
||||
RUN rm -r .next/cache
|
||||
|
||||
FROM node:21-alpine as runtime-deps
|
||||
FROM node:21-alpine AS runtime-deps
|
||||
|
||||
WORKDIR /usr/app
|
||||
COPY --from=base /usr/app/package.json /usr/app/package-lock.json /usr/app/next.config.js ./
|
||||
COPY --from=base /usr/app/package.json /usr/app/package-lock.json /usr/app/next.config.mjs ./
|
||||
COPY --from=base /usr/app/prisma ./prisma
|
||||
|
||||
RUN npm ci --omit=dev --omit=optional --ignore-scripts && \
|
||||
npx prisma generate
|
||||
|
||||
FROM node:21-alpine as runner
|
||||
FROM node:21-alpine AS runner
|
||||
|
||||
EXPOSE 3000/tcp
|
||||
WORKDIR /usr/app
|
||||
|
||||
COPY --from=base /usr/app/package.json /usr/app/package-lock.json /usr/app/next.config.js ./
|
||||
COPY --from=base /usr/app/package.json /usr/app/package-lock.json /usr/app/next.config.mjs ./
|
||||
COPY --from=runtime-deps /usr/app/node_modules ./node_modules
|
||||
COPY ./public ./public
|
||||
COPY ./scripts ./scripts
|
||||
|
||||
20
README.md
@@ -35,13 +35,24 @@ Spliit is a free and open source alternative to Splitwise. You can either use th
|
||||
|
||||
## Contribute
|
||||
|
||||
The project is open to contributions. Feel free to open an issue or even a pull-request!
|
||||
The project is open to contributions. Feel free to open an issue or even a pull-request!
|
||||
Join the discussion in [the Spliit Discord server](https://discord.gg/YSyVXbwvSY).
|
||||
|
||||
If you want to contribute financially and help us keep the application free and without ads, you can also:
|
||||
|
||||
- 💜 [Sponsor me (Sebastien)](https://github.com/sponsors/scastiel), or
|
||||
- 💙 [Make a small one-time donation](https://donate.stripe.com/28o3eh96G7hH8k89Ba).
|
||||
|
||||
### Translation
|
||||
|
||||
The project's translations are managed using [our Weblate project](https://hosted.weblate.org/projects/spliit/spliit/).
|
||||
You can easily add missing translations to the project or even add a new language!
|
||||
Here is the current state of translation:
|
||||
|
||||
<a href="https://hosted.weblate.org/engage/spliit/">
|
||||
<img src="https://hosted.weblate.org/widget/spliit/spliit/multi-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
## Run locally
|
||||
|
||||
1. Clone the repository (or fork it if you intend to contribute)
|
||||
@@ -57,6 +68,13 @@ If you want to contribute financially and help us keep the application free and
|
||||
3. Run `npm run start-container` to start the postgres and the spliit2 containers
|
||||
4. You can access the app by browsing to http://localhost:3000
|
||||
|
||||
## Health check
|
||||
|
||||
The application has a health check endpoint that can be used to check if the application is running and if the database is accessible.
|
||||
|
||||
- `GET /api/health/readiness` or `GET /api/health` - Check if the application is ready to serve requests, including database connectivity.
|
||||
- `GET /api/health/liveness` - Check if the application is running, but not necessarily ready to serve requests.
|
||||
|
||||
## Opt-in features
|
||||
|
||||
### Expense documents
|
||||
|
||||
15
compose.yaml
@@ -1,6 +1,7 @@
|
||||
services:
|
||||
app:
|
||||
image: spliit2:latest
|
||||
build: .
|
||||
image: spliit:latest
|
||||
ports:
|
||||
- 3000:3000
|
||||
env_file:
|
||||
@@ -8,11 +9,13 @@ services:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- spliit_network
|
||||
|
||||
db:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- 5432:5432
|
||||
expose:
|
||||
- 5432
|
||||
env_file:
|
||||
- container.env
|
||||
volumes:
|
||||
@@ -22,3 +25,9 @@ services:
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- spliit_network
|
||||
|
||||
networks:
|
||||
spliit_network:
|
||||
driver: bridge
|
||||
|
||||
21
eslint.config.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
import nextVitals from 'eslint-config-next/core-web-vitals'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
'.next/**',
|
||||
'out/**',
|
||||
'build/**',
|
||||
'next-env.d.ts',
|
||||
]),
|
||||
{
|
||||
rules: {
|
||||
'react-hooks/set-state-in-effect': 'off',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
export default eslintConfig
|
||||
18
jest.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Config } from 'jest'
|
||||
import nextJest from 'next/jest.js'
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||
dir: './',
|
||||
})
|
||||
|
||||
// Add any custom config to be passed to Jest
|
||||
const config: Config = {
|
||||
coverageProvider: 'v8',
|
||||
testEnvironment: 'jsdom',
|
||||
// Add more setup options before each test is run
|
||||
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||
}
|
||||
|
||||
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||
export default createJestConfig(config)
|
||||
459
messages/ca.json
Normal file
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Comparteix <strong>despeses</strong> amb <strong>família i amics</strong>",
|
||||
"description": "Benvinguda a <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Veure els grups",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grups"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Fet a Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Desenvolupat per <author>Sebastien Castiel</author> i <source>col·laboradors</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Despeses",
|
||||
"description": "Aquí trobaràs les despeses que has creat al teu grup.",
|
||||
"create": "Afegir despesa",
|
||||
"createFirst": "Crea la primera",
|
||||
"noExpenses": "El teu grup encara no té despeses.",
|
||||
"exportJson": "Exportar a JSON",
|
||||
"exportCsv": "Exportar a CSV",
|
||||
"searchPlaceholder": "Cerca una despesa.",
|
||||
"ActiveUserModal": {
|
||||
"title": "Qui ets?",
|
||||
"description": "Duige'ns quin participant ets, perquè puguem personalitzar com es mostra la informació.",
|
||||
"nobody": "No vull seleccionar a ningú",
|
||||
"save": "Desar canvis",
|
||||
"footer": "Aquesta configuració pot modificar-se amb posterioritat, a la configuració del grup."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Pròximament",
|
||||
"thisWeek": "Aquesta setmana",
|
||||
"earlierThisMonth": "A inicis d'aquest mes",
|
||||
"lastMonth": "El mes anterior",
|
||||
"earlierThisYear": "A inicis d'aquest any",
|
||||
"lastYear": "L'any passat",
|
||||
"older": "Més antics"
|
||||
},
|
||||
"export": "Exportar"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Pagada per <strong>{paidBy}</strong> per a <paidFor></paidFor>",
|
||||
"receivedBy": "Debuda per <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"yourBalance": "El teu balanç: balance:",
|
||||
"everyone": "totes",
|
||||
"notInvolved": "No ets implicat o implicada"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Els meus grups",
|
||||
"create": "Crear",
|
||||
"loadingRecent": "Arregant els grups recents…",
|
||||
"NoRecent": {
|
||||
"description": "No has format part de cap grup darrerament.",
|
||||
"create": "Crea'n un",
|
||||
"orAsk": "o demana a una amiga que et comparteixi l'enllaç a un grup ja existent"
|
||||
},
|
||||
"recent": "Grups recents",
|
||||
"starred": "Grups preferits",
|
||||
"archived": "Grups arxivats",
|
||||
"archive": "Arxivar el grup",
|
||||
"unarchive": "Desarxivar el grup",
|
||||
"removeRecent": "Suprimeix dels grups recents",
|
||||
"RecentRemovedToast": {
|
||||
"title": "El grup ha estat eliminat",
|
||||
"description": "El grup ha estat eliminat de la tebva llista de grups recents",
|
||||
"undoAlt": "Reverteix la suoressió del grup",
|
||||
"undo": "Desfer"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Afegir mitjançant un enllaç",
|
||||
"title": "Afegir un grup mitjançant un enllaç",
|
||||
"description": "Si t'han compartit un grup, pots enganxar aqui el seu enllaç per afegir-lo a la teva llista",
|
||||
"error": "Vatua l’olla! No podem trobar el grup des de l'enllaç que has proporcionat..."
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Aquest grup no existeix.",
|
||||
"link": "Anar als darrers grups visitats."
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informació del grup",
|
||||
"NameField": {
|
||||
"label": "Nom del grup",
|
||||
"placeholder": "Calçotada amb la colla",
|
||||
"description": "Afegeix un nom per al teu nou grup."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informació del grupo",
|
||||
"placeholder": "Quina informació és rellevant per les participants?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Símbol de la divisa",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Ho farem servir per mostrar el balanç."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participants",
|
||||
"description": "Afegeix el nom de totes les participants.",
|
||||
"protectedParticipant": "Aquestes participants ténen despeses al seu nom i no poden ser esborrades.",
|
||||
"new": "Nou",
|
||||
"add": "Afegir participants",
|
||||
"John": "Ermessenda",
|
||||
"Jane": "Guillem",
|
||||
"Jack": "Jaume"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Configuració local",
|
||||
"description": "Aquesta configuració s'estableix per aquest diposotiu i s'utilitzarà per personalitzar la teva experiència.",
|
||||
"ActiveUserField": {
|
||||
"label": "Usuària activa",
|
||||
"placeholder": "Selecciona una participant...",
|
||||
"none": "Cap",
|
||||
"description": "Usuària que paga les despeses de manera predeterminada."
|
||||
},
|
||||
"save": "Desar",
|
||||
"saving": "Desant",
|
||||
"create": "Crear",
|
||||
"creating": "Creant",
|
||||
"cancel": "Cancel·lar"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Divisa principal",
|
||||
"createDescription": "Totes les quantitats i els balanços es mostraràn en aquesta moneda.",
|
||||
"editDescription": "Totes les quantitas i balanços es mostraràn en aquesta moneda. Canviar açò NO convertirà despeses ja enregistrades, excepte quan la moneda tinga \"unitats menors\" diferents que la actual (per exemple, canviar de Dólars estadounidencs a Yens japonesos)",
|
||||
"customOption": "Personalitzat"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Afegir despesa",
|
||||
"edit": "Editar despesa",
|
||||
"TitleField": {
|
||||
"label": "Títol de la despesa",
|
||||
"placeholder": "Berenar-sopar",
|
||||
"description": "Introdueix una descripció per aquesta despesa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data de la despesa",
|
||||
"description": "Adegeix la data de la despesa."
|
||||
},
|
||||
"categoryFieldDescription": "Selecciona la categoria de la despesa.",
|
||||
"paidByField": {
|
||||
"label": "Pagat per",
|
||||
"description": "Selecciona la participant que ha fet el pagament."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Recurrència de la despesa",
|
||||
"description": "Selecciona amb quina freqüència cal que es repeteixi la despesa.",
|
||||
"none": "Puntual",
|
||||
"daily": "Diària",
|
||||
"weekly": "Setmanal",
|
||||
"monthly": "Mensual"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Pagat per a",
|
||||
"description": "Selecciona per a qui s'ha efectuat el pagament."
|
||||
},
|
||||
"splitModeDescription": "Selecciona com vols dividir la despesa.",
|
||||
"attachDescription": "Veure i adjuntar tiquets a la despesa.",
|
||||
"currencyField": {
|
||||
"label": "Moneda del ingrés",
|
||||
"description": "La moneda en la que l'ingrés va ser rebut."
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Crear despesa",
|
||||
"edit": "Editar despesa",
|
||||
"TitleField": {
|
||||
"label": "Títol de la despesa",
|
||||
"placeholder": "Llaunes del queviures",
|
||||
"description": "Afegeix una descripció per la despesa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data de la despesa",
|
||||
"description": "Afegeix la data en que es va realitzar la despesa."
|
||||
},
|
||||
"categoryFieldDescription": "Selecciona la cateogria de la despesa.",
|
||||
"paidByField": {
|
||||
"label": "Pagat per",
|
||||
"description": "Selecciona la participant que ha pagat la despesa.",
|
||||
"placeholder": "Tria un membre"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Pagado per a",
|
||||
"description": "Sleecciona per a qui s'ha pagat la despesa."
|
||||
},
|
||||
"splitModeDescription": "Selecciona com vols dividir la despesa.",
|
||||
"attachDescription": "eure i adjuntar tiquets a la despesa.",
|
||||
"currencyField": {
|
||||
"label": "Moneda de la despesa",
|
||||
"description": "La moneda en la que la despesa es va pagar."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Despesa recurrent",
|
||||
"description": "Tria la freqüència amb la que la despesa s'ha de repetir.",
|
||||
"none": "Cap",
|
||||
"daily": "Diàriament",
|
||||
"weekly": "Setmanalment",
|
||||
"monthly": "Mensualment"
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Quantitat"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Això és un reemborsament"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notes"
|
||||
},
|
||||
"selectNone": "No en seleccionis cap",
|
||||
"selectAll": "Selecciona'ls tots",
|
||||
"shares": "parts",
|
||||
"advancedOptions": "Opciones avanzadas",
|
||||
"SplitModeField": {
|
||||
"label": "Mode de divisió",
|
||||
"evenly": "Uniforme",
|
||||
"byShares": "Desigual: per parts",
|
||||
"byPercentage": "Desigual: per percentatge",
|
||||
"byAmount": "Desigual: Per quantitat",
|
||||
"saveAsDefault": "Desa com a mode preferit"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Esborrar",
|
||||
"title": "Esborrar la despesa?",
|
||||
"description": "Segur que vols suprimir aquesta despesa? Aquesta acció és irreversible.",
|
||||
"yes": "Sí",
|
||||
"cancel": "Cancel·lar"
|
||||
},
|
||||
"attachDocuments": "Adjuntar documents",
|
||||
"create": "Crear",
|
||||
"creating": "Creant",
|
||||
"save": "Desar",
|
||||
"saving": "Desant",
|
||||
"cancel": "Cancel·lar",
|
||||
"reimbursement": "Reemborsament",
|
||||
"conversionUnavailable": "Per a seleccionar una moneda diferent per despesa i convertir quantitats, tria una moneda no personalitzada per al grup.",
|
||||
"originalAmountField": {
|
||||
"label": "Quantitat a convertir"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Emprar les tases de Frankfurter",
|
||||
"useCustom": "Utilitzar tasa personalitzada",
|
||||
"label": "Tasa de conversió"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Aconseguint tases d'intercanvi…",
|
||||
"success": "Tases obtingudes:",
|
||||
"error": "Ups, no hem pogut aconseguit les tases mes recents.",
|
||||
"staleRate": "Utilizant la tasa:",
|
||||
"noRate": "Introdueix una tasa personalitzada ací.",
|
||||
"currencyNotFound": "Ups, Frankfurter no té la tasa per a la moneda triada aquest dia.",
|
||||
"noDate": "Introdueix la data de ka despesa per a aconseguir la tasa de conversió.",
|
||||
"dateMismatch": "Tases per a la data: {date}",
|
||||
"refresh": "Refrescar",
|
||||
"customRate": "Emprant tasa personalitzada"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "L'arxiu és massa gran",
|
||||
"description": "La mida màxima que pot tenir l'arxiu és de {maxSize}. El teu pesa {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error al carregar el document",
|
||||
"description": "Hi ha hagut un error al carregar el document. Torna a intentar-ho més tard o selecciona un altre arxiu.",
|
||||
"retry": "Tornar-ho a intentar"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Crear una despesa des d'un tiquet",
|
||||
"title": "Crear des del tiquet",
|
||||
"description": "Extreure la informació de despeses de la foto d'un tiquet.",
|
||||
"body": "Puja la foto d'un tiquet i l'escanejarem per extreure'n, si podem, la informaició de la despesa.",
|
||||
"selectImage": "Seleccionar la imatge…",
|
||||
"titleLabel": "Títol:",
|
||||
"categoryLabel": "Categoria:",
|
||||
"amountLabel": "Quantitat:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "A continuació, podràs editar la informació de les despeses.",
|
||||
"continue": "Continuar"
|
||||
},
|
||||
"unknown": "Desconegut",
|
||||
"TooBigToast": {
|
||||
"title": "L'arxiu és massa gran",
|
||||
"description": "La mida màxima que pot tenir l'arxiu és de {maxSize}. El teu pesa {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error al carregar el document",
|
||||
"description": "Hi ha hagut un error al carregar el document. Torna a intentar-ho més tard o selecciona un altre arxiu.",
|
||||
"retry": "Tornar-ho a intentar"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Balanç",
|
||||
"description": "L'import total que ha pagat o rebut cada participant.",
|
||||
"Reimbursements": {
|
||||
"title": "Proposta de reemborsaments",
|
||||
"description": "Suggerència per optimitzar els reemborsaments entre participants.",
|
||||
"noImbursements": "Sembla que el teu grup no necessita cap reemborsament 😁",
|
||||
"owes": "<strong>{from}</strong> deu a <strong>{to}</strong>",
|
||||
"markAsPaid": "Marcar com a pagat"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Estadístiques",
|
||||
"Totals": {
|
||||
"title": "Totals",
|
||||
"description": "Resum de les despeses de tot el grup.",
|
||||
"groupSpendings": "Despeses totals del grup",
|
||||
"groupEarnings": "Ingressos totals del grup",
|
||||
"yourSpendings": "Suma de les teves despeses",
|
||||
"yourEarnings": "Suma dels teus ingressos",
|
||||
"yourShare": "El teu percentatge"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Activitat",
|
||||
"description": "Aquí trobaràs totes les activitats recents del grup.",
|
||||
"noActivity": "No hi ha activitat recent a aquest grup.",
|
||||
"someone": "Algú",
|
||||
"settingsModified": "La configuració del grup ha estat modificada per <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Despesa <em>{expense}</em> creada per <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Despesa <em>{expense}</em> actualitzada per <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Despesa <em>{expense}</em> esborrada per <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Avui",
|
||||
"yesterday": "Ahir",
|
||||
"earlierThisWeek": "A inicis d'aquesta setmana",
|
||||
"lastWeek": "La setmana passada",
|
||||
"earlierThisMonth": "A inicis d'aquest mes",
|
||||
"lastMonth": "El mes passat",
|
||||
"earlierThisYear": "A inicis d'aquest any",
|
||||
"lastYear": "El darrer any",
|
||||
"older": "Més antigues"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informació",
|
||||
"description": "Utilitza aquest apartat per afegir informació rellevant per als participants del grup.",
|
||||
"empty": "Encara no hi ha informació sobre el grup."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Configuració"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Compartir",
|
||||
"description": "Comparteix l'enllaç al grup amb altres participants perquè puguin veure el grup i afegir-hi despeses.",
|
||||
"warning": "Alerta!",
|
||||
"warningHelp": "Tothom qui tingui l'enllaç del grup podrà veure i editar les despeses. Comparteix-lo amb cautela!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Introdueix com a mínim un caràcter.",
|
||||
"min2": "Introdueix com a mínim dos caràcters.",
|
||||
"max5": "Introdueix com a màxim cinc caràcters.",
|
||||
"max50": "Introdueix com a màxim cinquanta caràcters.",
|
||||
"duplicateParticipantName": "Una altra participant ja té el mateix nom",
|
||||
"titleRequired": "Si us plau, afegeix un títol",
|
||||
"invalidNumber": "Número invàlid",
|
||||
"amountRequired": "Cal introduïr un import",
|
||||
"amountNotZero": "L'import no por ser zero.",
|
||||
"amountTenMillion": "L'import ha de ser inferior a 10.000.000.",
|
||||
"paidByRequired": "Cal seleccionar una participant",
|
||||
"paidForMin1": "La despesa ha de ser pagada com a mínim per a una participant",
|
||||
"noZeroShares": "Totes les participacions han de ser superiors a 0",
|
||||
"amountSum": "La suma dels imports ha de ser igual a la suma de la despesa",
|
||||
"percentageSum": "La suma dels percentatges ha de ser igual a 100",
|
||||
"ratePositive": "La tasa ha de ser estrictament major que zero."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Cercar categoria...",
|
||||
"noCategory": "Categoria no trobada!",
|
||||
"Uncategorized": {
|
||||
"heading": "Sense categoria",
|
||||
"General": "General",
|
||||
"Payment": "Pagament"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Oci",
|
||||
"Entertainment": "Oci",
|
||||
"Games": "Jocs",
|
||||
"Movies": "Pel·lícules",
|
||||
"Music": "Música",
|
||||
"Sports": "Esport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Menjar i beure",
|
||||
"Food and Drink": "Menjar i beure",
|
||||
"Dining Out": "Menjar fora",
|
||||
"Groceries": "Menjar",
|
||||
"Liquor": "Licors"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Casa",
|
||||
"Home": "Casa",
|
||||
"Electronics": "Elecrtònica",
|
||||
"Furniture": "Mobles",
|
||||
"Household Supplies": "Subministres",
|
||||
"Maintenance": " Manteniment",
|
||||
"Mortgage": "Hipoteca",
|
||||
"Pets": "Mascotes",
|
||||
"Rent": "Lloguer",
|
||||
"Services": "Serveis"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Vida",
|
||||
"Childcare": "Cura de criatures",
|
||||
"Clothing": "Roba",
|
||||
"Education": "Ensenyament",
|
||||
"Gifts": "Regals",
|
||||
"Insurance": "Assegurança",
|
||||
"Medical Expenses": "Despeses mèdiques",
|
||||
"Taxes": "Impostos",
|
||||
"Donation": "Donació"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transport",
|
||||
"Transportation": "Transport",
|
||||
"Bicycle": "Bicicleta",
|
||||
"Bus/Train": "Autobús/Tren",
|
||||
"Car": "Cotxe",
|
||||
"Gas/Fuel": "Gasolina/Combustible",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Aparcament",
|
||||
"Plane": "Avió",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilitats",
|
||||
"Utilities": "Utilitats",
|
||||
"Cleaning": "Neteja",
|
||||
"Electricity": "Electricitat",
|
||||
"Heat/Gas": "Calefacció/Gas",
|
||||
"Trash": "Ecombraries",
|
||||
"TV/Phone/Internet": "TV/Telèfon/Internet",
|
||||
"Water": "Aigua"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Buscar moneda...",
|
||||
"noCurrency": "No s'han trobat monedes.",
|
||||
"custom": {
|
||||
"heading": "Personalitzat"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Més comunes"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Altres monedes"
|
||||
}
|
||||
}
|
||||
}
|
||||
399
messages/cs-CZ.json
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Sdílejte <strong>výdaje</strong> s <strong>přáteli a rodinou</strong>",
|
||||
"description": "Vítejte ve své nové instanci <strong>Spliitu</strong>!",
|
||||
"button": {
|
||||
"groups": "Přejít na skupiny",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Skupiny"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Vyrobeno v Montréalu, Québec 🇨🇦",
|
||||
"builtBy": "Vytvořil <author>Sebastien Castiel</author> a <source>další přispěvatelé</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Výdaje",
|
||||
"description": "Zde jsou výdaje, které jste vytvořili pro svou skupinu.",
|
||||
"create": "Vytvořit výdaj",
|
||||
"createFirst": "Vytvořit první",
|
||||
"noExpenses": "Vaše skupina zatím neobsahuje žádné výdaje.",
|
||||
"export": "Exportovat",
|
||||
"exportJson": "Exportovat do JSON",
|
||||
"exportCsv": "Exportovat do CSV",
|
||||
"searchPlaceholder": "Hledat výdaj…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Kdo jste?",
|
||||
"description": "Řekněte nám, který účastník jste, abychom mohli přizpůsobit zobrazení informací.",
|
||||
"nobody": "Nechci nikoho vybírat",
|
||||
"save": "Uložit změny",
|
||||
"footer": "Toto nastavení můžete později změnit v nastavení skupiny."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Nadcházející",
|
||||
"thisWeek": "Tento týden",
|
||||
"earlierThisMonth": "Dříve tento měsíc",
|
||||
"lastMonth": "Minulý měsíc",
|
||||
"earlierThisYear": "Dříve tento rok",
|
||||
"lastYear": "Minulý rok",
|
||||
"older": "Starší"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Zaplatil/a <strong>{paidBy}</strong> za <paidFor></paidFor>",
|
||||
"receivedBy": "Obdržel/a <strong>{paidBy}</strong> od <paidFor></paidFor>",
|
||||
"yourBalance": "Váš zůstatek:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Moje skupiny",
|
||||
"create": "Vytvořit",
|
||||
"loadingRecent": "Načítání nedávných skupin…",
|
||||
"NoRecent": {
|
||||
"description": "Nedávno jste nenavštívili žádnou skupinu.",
|
||||
"create": "Vytvořit skupinu",
|
||||
"orAsk": "nebo požádejte přítele, aby vám poslal odkaz na existující."
|
||||
},
|
||||
"recent": "Nedávné skupiny",
|
||||
"starred": "Oblíbené skupiny",
|
||||
"archived": "Archivované skupiny",
|
||||
"archive": "Archivovat skupinu",
|
||||
"unarchive": "Zrušit archivaci skupiny",
|
||||
"removeRecent": "Odebrat z nedávných skupin",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Skupina byla odebrána",
|
||||
"description": "Skupina byla odebrána ze seznamu vašich nedávných skupin.",
|
||||
"undoAlt": "Vrátit zpět odebrání skupiny",
|
||||
"undo": "Vrátit zpět"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Přidat pomocí URL",
|
||||
"title": "Přidat skupinu pomocí URL",
|
||||
"description": "Pokud s vámi byla skupina sdílena, můžete sem vložit její URL a přidat ji do svého seznamu.",
|
||||
"error": "Bohužel se nám nepodařilo najít skupinu podle zadané URL…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Tato skupina neexistuje.",
|
||||
"link": "Přejít na nedávno navštívené skupiny"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informace o skupině",
|
||||
"NameField": {
|
||||
"label": "Název skupiny",
|
||||
"placeholder": "Letní dovolená",
|
||||
"description": "Zadejte název své skupiny."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informace o skupině",
|
||||
"placeholder": "Jaké informace jsou důležité pro účastníky skupiny?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Symbol měny",
|
||||
"placeholder": "CZK, Kč, $, €, £…",
|
||||
"description": "Použijeme ho pro zobrazení částek."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Účastníci",
|
||||
"description": "Zadejte jméno každého účastníka.",
|
||||
"protectedParticipant": "Tento účastník je součástí výdajů a nelze jej odebrat.",
|
||||
"new": "Nový",
|
||||
"add": "Přidat účastníka",
|
||||
"John": "Jan",
|
||||
"Jane": "Jana",
|
||||
"Jack": "Jakub"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Místní nastavení",
|
||||
"description": "Tato nastavení jsou specifická pro toto zařízení a slouží k přizpůsobení vašeho zážitku.",
|
||||
"ActiveUserField": {
|
||||
"label": "Aktivní uživatel",
|
||||
"placeholder": "Vyberte účastníka",
|
||||
"none": "Žádný",
|
||||
"description": "Uživatel použitý jako výchozí pro placení výdajů."
|
||||
},
|
||||
"save": "Uložit",
|
||||
"saving": "Ukládání…",
|
||||
"create": "Vytvořit",
|
||||
"creating": "Vytváření…",
|
||||
"cancel": "Zrušit"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Vytvořit příjem",
|
||||
"edit": "Upravit příjem",
|
||||
"TitleField": {
|
||||
"label": "Název příjmu",
|
||||
"placeholder": "Pondělní večerní restaurace",
|
||||
"description": "Zadejte popis příjmu."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum příjmu",
|
||||
"description": "Zadejte datum, kdy byl příjem přijat."
|
||||
},
|
||||
"categoryFieldDescription": "Vyberte kategorii příjmu.",
|
||||
"paidByField": {
|
||||
"label": "Obdržel/a",
|
||||
"description": "Vyberte účastníka, který příjem obdržel."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Obdrženo pro",
|
||||
"description": "Vyberte, pro koho byl příjem obdržen."
|
||||
},
|
||||
"splitModeDescription": "Vyberte, jak rozdělit příjem.",
|
||||
"attachDescription": "Zobrazit a připojit účtenky k příjmu."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Vytvořit výdaj",
|
||||
"edit": "Upravit výdaj",
|
||||
"TitleField": {
|
||||
"label": "Název výdaje",
|
||||
"placeholder": "Pondělní večerní restaurace",
|
||||
"description": "Zadejte popis výdaje."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum výdaje",
|
||||
"description": "Zadejte datum, kdy byl výdaj zaplacen."
|
||||
},
|
||||
"categoryFieldDescription": "Vyberte kategorii výdaje.",
|
||||
"paidByField": {
|
||||
"label": "Zaplatil/a",
|
||||
"description": "Vyberte účastníka, který výdaj zaplatil."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Opakování výdaje",
|
||||
"description": "Vyberte, jak často se má výdaj opakovat.",
|
||||
"none": "Žádné",
|
||||
"daily": "Denně",
|
||||
"weekly": "Týdně",
|
||||
"monthly": "Měsíčně"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Zaplaceno pro",
|
||||
"description": "Vyberte, pro koho byl výdaj zaplacen."
|
||||
},
|
||||
"splitModeDescription": "Vyberte, jak rozdělit výdaj.",
|
||||
"attachDescription": "Zobrazit a připojit účtenky k výdaji."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Částka"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Toto je proplacení"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategorie"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Poznámky"
|
||||
},
|
||||
"selectNone": "Nevybrat nic",
|
||||
"selectAll": "Vybrat vše",
|
||||
"shares": "podíl(y)",
|
||||
"advancedOptions": "Pokročilé možnosti rozdělení…",
|
||||
"SplitModeField": {
|
||||
"label": "Způsob rozdělení",
|
||||
"evenly": "Rovnoměrně",
|
||||
"byShares": "Nerovnoměrně – podle podílů",
|
||||
"byPercentage": "Nerovnoměrně – podle procent",
|
||||
"byAmount": "Nerovnoměrně – podle částky",
|
||||
"saveAsDefault": "Uložit jako výchozí možnosti rozdělení"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Smazat",
|
||||
"title": "Smazat tento výdaj?",
|
||||
"description": "Opravdu chcete tento výdaj smazat? Tato akce je nevratná.",
|
||||
"yes": "Ano",
|
||||
"cancel": "Zrušit"
|
||||
},
|
||||
"attachDocuments": "Připojit dokumenty",
|
||||
"create": "Vytvořit",
|
||||
"creating": "Vytváření…",
|
||||
"save": "Uložit",
|
||||
"saving": "Ukládání…",
|
||||
"cancel": "Zrušit",
|
||||
"reimbursement": "Proplacení"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Soubor je příliš velký",
|
||||
"description": "Maximální velikost souboru, který můžete nahrát, je {maxSize}. Váš má {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Chyba při nahrávání dokumentu",
|
||||
"description": "Při nahrávání dokumentu došlo k chybě. Zkuste to prosím později nebo vyberte jiný soubor.",
|
||||
"retry": "Zkusit znovu"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Vytvořit výdaj z účtenky",
|
||||
"title": "Vytvořit z účtenky",
|
||||
"description": "Získat informace o výdaji z fotografie účtenky.",
|
||||
"body": "Nahrajte fotografii účtenky a my se pokusíme z ní získat informace o výdaji.",
|
||||
"selectImage": "Vybrat obrázek…",
|
||||
"titleLabel": "Název:",
|
||||
"categoryLabel": "Kategorie:",
|
||||
"amountLabel": "Částka:",
|
||||
"dateLabel": "Datum:",
|
||||
"editNext": "Informace o výdaji budete moci upravit v dalším kroku.",
|
||||
"continue": "Pokračovat"
|
||||
},
|
||||
"unknown": "Neznámé",
|
||||
"TooBigToast": {
|
||||
"title": "Soubor je příliš velký",
|
||||
"description": "Maximální velikost souboru, který můžete nahrát, je {maxSize}. Váš má {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Chyba při nahrávání dokumentu",
|
||||
"description": "Při nahrávání dokumentu došlo k chybě. Zkuste to prosím později nebo vyberte jiný soubor.",
|
||||
"retry": "Zkusit znovu"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Zůstatky",
|
||||
"description": "Toto je částka, kterou každý účastník zaplatil nebo za kterou bylo zaplaceno.",
|
||||
"Reimbursements": {
|
||||
"title": "Navrhovaná proplacení",
|
||||
"description": "Zde jsou návrhy na optimalizovaná proplacení mezi účastníky.",
|
||||
"noImbursements": "Vypadá to, že vaše skupina nepotřebuje žádné proplacení 😁",
|
||||
"owes": "<strong>{from}</strong> dluží <strong>{to}</strong>",
|
||||
"markAsPaid": "Označit jako zaplaceno"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistiky",
|
||||
"Totals": {
|
||||
"title": "Celkové součty",
|
||||
"description": "Shrnutí výdajů celé skupiny.",
|
||||
"groupSpendings": "Celkové výdaje skupiny",
|
||||
"groupEarnings": "Celkové příjmy skupiny",
|
||||
"yourSpendings": "Vaše celkové výdaje",
|
||||
"yourEarnings": "Vaše celkové příjmy",
|
||||
"yourShare": "Váš celkový podíl"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Aktivita",
|
||||
"description": "Přehled veškeré aktivity v této skupině.",
|
||||
"noActivity": "Ve vaší skupině zatím není žádná aktivita.",
|
||||
"someone": "Někdo",
|
||||
"settingsModified": "Nastavení skupiny upravil <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Výdaj <em>{expense}</em> vytvořil <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Výdaj <em>{expense}</em> upravil <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Výdaj <em>{expense}</em> smazal <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Dnes",
|
||||
"yesterday": "Včera",
|
||||
"earlierThisWeek": "Dříve tento týden",
|
||||
"lastWeek": "Minulý týden",
|
||||
"earlierThisMonth": "Dříve tento měsíc",
|
||||
"lastMonth": "Minulý měsíc",
|
||||
"earlierThisYear": "Dříve tento rok",
|
||||
"lastYear": "Minulý rok",
|
||||
"older": "Starší"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informace",
|
||||
"description": "Použijte toto místo k přidání informací, které mohou být důležité pro účastníky skupiny.",
|
||||
"empty": "Zatím žádné informace o skupině."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Nastavení"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Sdílet",
|
||||
"description": "Aby ostatní účastníci mohli vidět skupinu a přidávat výdaje, sdílejte s nimi její URL.",
|
||||
"warning": "Varování!",
|
||||
"warningHelp": "Každý, kdo má URL skupiny, bude moci zobrazit a upravovat výdaje. Sdílejte opatrně!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Zadejte alespoň jeden znak.",
|
||||
"min2": "Zadejte alespoň dva znaky.",
|
||||
"max5": "Zadejte nejvýše pět znaků.",
|
||||
"max50": "Zadejte nejvýše 50 znaků.",
|
||||
"duplicateParticipantName": "Jiný účastník už toto jméno má.",
|
||||
"titleRequired": "Zadejte prosím název.",
|
||||
"invalidNumber": "Neplatné číslo.",
|
||||
"amountRequired": "Musíte zadat částku.",
|
||||
"amountNotZero": "Částka nesmí být nula.",
|
||||
"amountTenMillion": "Částka musí být nižší než 10 000 000.",
|
||||
"paidByRequired": "Musíte vybrat účastníka.",
|
||||
"paidForMin1": "Výdaj musí být zaplacen alespoň za jednoho účastníka.",
|
||||
"noZeroShares": "Všechny podíly musí být větší než 0.",
|
||||
"amountSum": "Součet částek se musí rovnat částce výdaje.",
|
||||
"percentageSum": "Součet procent musí být 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Hledat kategorii...",
|
||||
"noCategory": "Nebyla nalezena žádná kategorie.",
|
||||
"Uncategorized": {
|
||||
"heading": "Nezařazené",
|
||||
"General": "Obecné",
|
||||
"Payment": "Platba"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Zábava",
|
||||
"Entertainment": "Zábava",
|
||||
"Games": "Hry",
|
||||
"Movies": "Filmy",
|
||||
"Music": "Hudba",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Jídlo a pití",
|
||||
"Food and Drink": "Jídlo a pití",
|
||||
"Dining Out": "Restaurace",
|
||||
"Groceries": "Potraviny",
|
||||
"Liquor": "Alkohol"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Domov",
|
||||
"Home": "Domov",
|
||||
"Electronics": "Elektronika",
|
||||
"Furniture": "Nábytek",
|
||||
"Household Supplies": "Domácí potřeby",
|
||||
"Maintenance": "Údržba",
|
||||
"Mortgage": "Hypotéka",
|
||||
"Pets": "Domácí mazlíčci",
|
||||
"Rent": "Nájem",
|
||||
"Services": "Služby"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Život",
|
||||
"Childcare": "Péče o děti",
|
||||
"Clothing": "Oblečení",
|
||||
"Donation": "Dar",
|
||||
"Education": "Vzdělání",
|
||||
"Gifts": "Dárky",
|
||||
"Insurance": "Pojištění",
|
||||
"Medical Expenses": "Zdravotní výdaje",
|
||||
"Taxes": "Daně"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Doprava",
|
||||
"Transportation": "Doprava",
|
||||
"Bicycle": "Kolo",
|
||||
"Bus/Train": "Autobus/Vlak",
|
||||
"Car": "Auto",
|
||||
"Gas/Fuel": "Benzín/Palivo",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parkování",
|
||||
"Plane": "Letadlo",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Služby",
|
||||
"Utilities": "Služby",
|
||||
"Cleaning": "Úklid",
|
||||
"Electricity": "Elektřina",
|
||||
"Heat/Gas": "Topení/Plyn",
|
||||
"Trash": "Odpad",
|
||||
"TV/Phone/Internet": "TV/Telefon/Internet",
|
||||
"Water": "Voda"
|
||||
}
|
||||
}
|
||||
}
|
||||
459
messages/de-DE.json
Normal file
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Teile <strong>Ausgaben</strong> mit <strong>Freunden & Familie</strong>",
|
||||
"description": "Willkommen zu deiner neuen <strong>Spliit</strong>-Instanz!",
|
||||
"button": {
|
||||
"groups": "Zu den Gruppen",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Gruppen"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Entwickelt in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Erstellt von <author>Sebastien Castiel</author> und <source>Mitwirkenden</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Ausgaben",
|
||||
"description": "Hier sind die Ausgaben, die du für deine Gruppe erstellt hast.",
|
||||
"create": "Ausgabe hinzufügen",
|
||||
"createFirst": "Erstelle die Erste",
|
||||
"noExpenses": "Deine Gruppe hat noch keine Ausgaben.",
|
||||
"exportJson": "Als JSON exportieren",
|
||||
"exportCsv": "Als CSV exportieren",
|
||||
"searchPlaceholder": "Suche nach einer Ausgabe…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Wer bist du?",
|
||||
"description": "Sag uns, welcher Teilnehmer du bist, um die angezeigten Informationen auf dich anzupassen.",
|
||||
"nobody": "Ich will niemanden auswählen",
|
||||
"save": "Änderungen speichern",
|
||||
"footer": "Diese Einstellung kann später in den Gruppeneinstellungen geändert werden."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Bevorstehend",
|
||||
"thisWeek": "Diese Woche",
|
||||
"earlierThisMonth": "Diesen Monat",
|
||||
"lastMonth": "Letzten Monat",
|
||||
"earlierThisYear": "Dieses Jahr",
|
||||
"lastYear": "Letztes Jahr",
|
||||
"older": "Älter"
|
||||
},
|
||||
"export": "Exportieren"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Gezahlt von <strong>{paidBy}</strong> für <paidFor></paidFor>",
|
||||
"receivedBy": "Empfangen von <strong>{paidBy}</strong> für <paidFor></paidFor>",
|
||||
"yourBalance": "Deine Bilanz:",
|
||||
"everyone": "jeder",
|
||||
"notInvolved": "Du bist nicht involviert"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Meine Gruppen",
|
||||
"create": "Erstellen",
|
||||
"loadingRecent": "Lade letzte Gruppen…",
|
||||
"NoRecent": {
|
||||
"description": "Du hast in der letzten Zeit keine Gruppe besucht.",
|
||||
"create": "Erstelle eine",
|
||||
"orAsk": "oder bitte einen Freund, dir einen Link zu einer Existierenden zu schicken."
|
||||
},
|
||||
"recent": "Letzte Gruppen",
|
||||
"starred": "Favorisierte Gruppen",
|
||||
"archived": "Archivierte Gruppen",
|
||||
"archive": "Gruppe archivieren",
|
||||
"unarchive": "Gruppe wiederherstellen",
|
||||
"removeRecent": "Aus letzten Gruppen entfernen",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Gruppe wurde entfernt",
|
||||
"description": "Die Gruppe wurde von deiner Liste der letzten Gruppen entfernt.",
|
||||
"undoAlt": "Gruppe entfernen rückgängig machen",
|
||||
"undo": "Rückgängig machen"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Mit URL hinzufügen",
|
||||
"title": "Gruppe mit URL hinzufügen",
|
||||
"description": "Wenn eine Gruppe mit dir geteilt wurde, kannst du ihre URL hier einfügen, um sie zu deiner Liste hinzuzufügen.",
|
||||
"error": "Ups, wir können die Gruppe mit der angegebenen URL nicht finden…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Diese Gruppe existiert nicht.",
|
||||
"link": "Gehe zu zuletzt besuchten Gruppen"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Gruppeninformationen",
|
||||
"NameField": {
|
||||
"label": "Gruppenname",
|
||||
"placeholder": "Sommerurlaub",
|
||||
"description": "Gib deiner Gruppe einen Namen."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Gruppeninformationen",
|
||||
"placeholder": "Welche Informationen sind relevant für Gruppenmitglieder?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Währungssymbol",
|
||||
"placeholder": "€, $, £…",
|
||||
"description": "Wir benutzen es, um Beträge anzuzeigen."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Mitglieder",
|
||||
"description": "Füge einen Namen für jedes Gruppenmitglied hinzu.",
|
||||
"protectedParticipant": "Dieses Mitglied ist Teil der Ausgaben und kann nicht entfernt werden.",
|
||||
"new": "Neu",
|
||||
"add": "Mitglied hinzufügen",
|
||||
"John": "Johannes",
|
||||
"Jane": "Janina",
|
||||
"Jack": "Jakob"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Lokale Einstellungen",
|
||||
"description": "Dies sind Einstellungen pro Gerät, die verwendet werden, um deine Benutzererfahrung zu verbessern.",
|
||||
"ActiveUserField": {
|
||||
"label": "Aktiver Nutzer",
|
||||
"placeholder": "Wähle ein Mitglied",
|
||||
"none": "Keiner",
|
||||
"description": "Standardnutzer, der die Ausgaben übernimmt."
|
||||
},
|
||||
"save": "Speichern",
|
||||
"saving": "Speichert…",
|
||||
"create": "Erstellen",
|
||||
"creating": "Erstellt…",
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Hauptwährung",
|
||||
"createDescription": "Alle Beträge und Salden werden in dieser Währung angegeben.",
|
||||
"customOption": "benutzerdefiniert",
|
||||
"editDescription": "Alle Beträge und Salden werden in dieser Währung angegeben. Bei Änderung dieser, werden bereits eingegebene Ausgaben NICHT umgerechnet, es sei denn, die Währung hat andere \"kleinere Einheiten\" als die aktuelle (z. B. Wechsel von US-Dollar zu Japanischem Yen)"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Einnahme erstellen",
|
||||
"edit": "Einnahme bearbeiten",
|
||||
"TitleField": {
|
||||
"label": "Titel der Einnahme",
|
||||
"placeholder": "Montagabend Restaurant",
|
||||
"description": "Füge eine Beschreibung für die Einnahme hinzu."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum der Einnahme",
|
||||
"description": "Füge ein Datum hinzu für wann die Einnahme erhalten wurde."
|
||||
},
|
||||
"categoryFieldDescription": "Wähle die Kategorie der Einnahme.",
|
||||
"paidByField": {
|
||||
"label": "Empfangen von",
|
||||
"description": "Wähle das Mitglied, das die Einnahme erhalten hat."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Wiederholung der Einnahme",
|
||||
"description": "Wähle aus, wie oft die Einnahme wiederholt werden soll.",
|
||||
"none": "Keine Wiederholung",
|
||||
"daily": "Täglich",
|
||||
"weekly": "Wöchentlich",
|
||||
"monthly": "Monatlich"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Empfangen für",
|
||||
"description": "Wähle für wen die Einnahme empfangen wurde."
|
||||
},
|
||||
"splitModeDescription": "Wähle, wie die Einnahme aufgeteilt werden soll.",
|
||||
"attachDescription": "Füge der Einnahme einen Beleg hinzu.",
|
||||
"currencyField": {
|
||||
"label": "Währung der Einnahme",
|
||||
"description": "Die Währung, in der die Einnahmen erhalten wurden."
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Ausgabe erstellen",
|
||||
"edit": "Ausgabe bearbeiten",
|
||||
"TitleField": {
|
||||
"label": "Titel der Ausgabe",
|
||||
"placeholder": "Montagabend Restaurant",
|
||||
"description": "Füge eine Beschreibung für die Ausgabe hinzu."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum der Ausgabe",
|
||||
"description": "Füge das Datum ein, zu dem die Ausgabe getätigt wurde."
|
||||
},
|
||||
"categoryFieldDescription": "Wähle eine Kategorie für die Ausgabe.",
|
||||
"paidByField": {
|
||||
"label": "Gezahlt von",
|
||||
"placeholder": "Wähle ein Mitglied",
|
||||
"description": "Wähle das Mitglied, das die Ausgabe bezahlt hat."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Wiederholung der Ausgabe",
|
||||
"description": "Wähle aus, wie oft die Ausgabe wiederholt werden soll.",
|
||||
"none": "Keine Wiederholung",
|
||||
"daily": "Täglich",
|
||||
"weekly": "Wöchentlich",
|
||||
"monthly": "Monatlich"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Gezahlt für",
|
||||
"description": "Wähle für wen die Ausgabe gezahlt wurde."
|
||||
},
|
||||
"splitModeDescription": "Wähle, wie die Ausgabe aufgeteilt werden soll.",
|
||||
"attachDescription": "Füge der Ausgabe einen Beleg hinzu.",
|
||||
"currencyField": {
|
||||
"label": "Währung der Ausgabe",
|
||||
"description": "Die Währung, in der die Ausgabe bezahlt wurde."
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Betrag"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Das ist eine Rückzahlung"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategorie"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notizen"
|
||||
},
|
||||
"selectNone": "Keine auswählen",
|
||||
"selectAll": "Alle auswählen",
|
||||
"shares": "Anteil(e)",
|
||||
"advancedOptions": "Fortgeschrittene Aufteilungsoptionen…",
|
||||
"SplitModeField": {
|
||||
"label": "Aufteilungsart",
|
||||
"evenly": "Gleich verteilt",
|
||||
"byShares": "Ungleich – Nach Anteilen",
|
||||
"byPercentage": "Ungleich – Prozentual",
|
||||
"byAmount": "Ungleich – Nach Betrag",
|
||||
"saveAsDefault": "Als Standardoptionen zur Aufteilung speichern"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Löschen",
|
||||
"title": "Diese Ausgabe löschen?",
|
||||
"description": "Willst du diese Ausgabe wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"yes": "Ja",
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
"attachDocuments": "Dokument hinzufügen",
|
||||
"create": "Erstellen",
|
||||
"creating": "Erstellt…",
|
||||
"save": "Speichern",
|
||||
"saving": "Speichert…",
|
||||
"cancel": "Abbrechen",
|
||||
"reimbursement": "Rückzahlung",
|
||||
"conversionUnavailable": "Um für jede Ausgabe eine andere Währung festzulegen und Beträge umzurechnen, wählen Sie eine nicht benutzerdefinierte Währung für die Gruppe aus.",
|
||||
"originalAmountField": {
|
||||
"label": "Umzurechnender Betrag"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"customRate": "Benutzerdefinierte Rate verwenden",
|
||||
"loading": "Wechselkurse abrufen…",
|
||||
"success": "Erhaltene Zinssätze:",
|
||||
"error": "Oops, wir konnten die aktuellsten Zinssätze nicht abrufen.",
|
||||
"staleRate": "Verwendeter Zinssatz:",
|
||||
"noRate": "Geben Sie unten einen benutzerdefinierten Zinssatz ein.",
|
||||
"noDate": "Geben Sie das Ausgabedatum ein, um einen Umrechnungskurs zu erhalten.",
|
||||
"dateMismatch": "Zinssätze von Datum: {date}",
|
||||
"refresh": "Aktualisiere",
|
||||
"currencyNotFound": "Oops, Frankfurter hat für diese Währung an diesem Tag keinen Zinssatz."
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useCustom": "Benutze individuelle Rate",
|
||||
"label": "Wechselkurs",
|
||||
"useApi": "Benutze Zinssätze von Frankfurter"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Die Datei ist zu groß",
|
||||
"description": "Die maximale Dateigröße ist {maxSize}. Deine ist {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Fehler beim Hochladen der Datei",
|
||||
"description": "Beim Hochladen der Datei ist etwas schiefgelaufen. Versuche es später nochmal oder wähle eine andere Datei.",
|
||||
"retry": "Wiederholen"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Ausgabe von Rechnungsbeleg erstellen",
|
||||
"title": "Von Rechnungsbeleg erstellen",
|
||||
"description": "Ausgabeninformationen von einem Foto einer Rechnung lesen.",
|
||||
"body": "Lade ein Foto der Rechnung hoch und wir versuchen die Ausgabeinformationen zu extrahieren.",
|
||||
"selectImage": "Bild wählen…",
|
||||
"titleLabel": "Titel:",
|
||||
"categoryLabel": "Kategorie:",
|
||||
"amountLabel": "Betrag:",
|
||||
"dateLabel": "Datum:",
|
||||
"editNext": "Als nächstes kannst du die Informationen zur Ausgabe editieren.",
|
||||
"continue": "Weiter"
|
||||
},
|
||||
"unknown": "Unbekannt",
|
||||
"TooBigToast": {
|
||||
"title": "Die Datei ist zu groß",
|
||||
"description": "Die maximale Dateigröße ist {maxSize}. Deine ist {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Fehler beim Hochladen der Datei",
|
||||
"description": "Beim Hochladen der Datei ist etwas schiefgelaufen. Versuche es später nochmal oder wähle eine andere Datei.",
|
||||
"retry": "Wiederholen"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Bilanz",
|
||||
"description": "Das sind die Beträge, die jedes Mitglied bezahlt oder empfangen hat.",
|
||||
"Reimbursements": {
|
||||
"title": "Vorgeschlagene Rückzahlungen",
|
||||
"description": "Hier sind Vorschläge für optimierte Rückzahlungen zwischen Mitgliedern.",
|
||||
"noImbursements": "Es sieht aus, als seien in der Gruppe keine Rückzahlungen nötig 😁",
|
||||
"owes": "<strong>{from}</strong> schuldet <strong>{to}</strong>",
|
||||
"markAsPaid": "Als gezahlt markieren"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistiken",
|
||||
"Totals": {
|
||||
"title": "Gesamtausgaben",
|
||||
"description": "Zusammenfassung der Ausgaben der gesamten Gruppe.",
|
||||
"groupSpendings": "Gesamte Ausgaben der Gruppe",
|
||||
"groupEarnings": "Gesamte Einnahmen der Gruppe",
|
||||
"yourSpendings": "Deine gesamten Ausgaben",
|
||||
"yourEarnings": "Deine gesamten Einnahmen",
|
||||
"yourShare": "Dein gesamter Anteil"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Aktivitäten",
|
||||
"description": "Zusammenfassung aller Aktivitäten in dieser Gruppe.",
|
||||
"noActivity": "Es gab noch keine Aktivität in dieser Gruppe.",
|
||||
"someone": "Jemand",
|
||||
"settingsModified": "Die Gruppeneinstellungen wurden von <strong>{participant}</strong> verändert.",
|
||||
"expenseCreated": "Ausgabe <em>{expense}</em> wurde von <strong>{participant}</strong> erstellt.",
|
||||
"expenseUpdated": "Ausgabe <em>{expense}</em> wurde von <strong>{participant}</strong> aktualisiert.",
|
||||
"expenseDeleted": "Ausgabe <em>{expense}</em> wurde von <strong>{participant}</strong> gelöscht.",
|
||||
"Groups": {
|
||||
"today": "Heute",
|
||||
"yesterday": "Gestern",
|
||||
"earlierThisWeek": "Anfang dieser Woche",
|
||||
"lastWeek": "Letze Woche",
|
||||
"earlierThisMonth": "Diesen Monat",
|
||||
"lastMonth": "Letzen Monat",
|
||||
"earlierThisYear": "Dieses Jahr",
|
||||
"lastYear": "Letztes Jahr",
|
||||
"older": "Älter"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informationen",
|
||||
"description": "Nutze diesen Ort, um Informationen hinzuzufügen, die für die Gruppenmitglieder wichtig sein könnten.",
|
||||
"empty": "Noch keine Gruppeninformationen vorhanden."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Einstellungen"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Teilen",
|
||||
"description": "Teile die URL, damit andere Mitglieder die Gruppe sehen und Ausgaben hinzufügen können.",
|
||||
"warning": "Achtung!",
|
||||
"warningHelp": "Jede Person mit der Gruppen-URL kann Ausgaben sehen und editieren. Teile den Link mit Bedacht!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Gib mindestens ein Zeichen ein.",
|
||||
"min2": "Gib mindestens zwei Zeichen ein.",
|
||||
"max5": "Gib maximal fünf Zeichen ein.",
|
||||
"max50": "Gib maximal 50 Zeichen ein.",
|
||||
"duplicateParticipantName": "Der Name ist bereits an ein anderes Gruppenmitglied vergeben.",
|
||||
"titleRequired": "Bitte gib einen Titel an.",
|
||||
"invalidNumber": "Zahl nicht valide.",
|
||||
"amountRequired": "Du musst einen Betrag angeben.",
|
||||
"amountNotZero": "Der Betrag darf nicht 0 sein.",
|
||||
"amountTenMillion": "Der Betrag muss kleiner als 10.000.000 sein.",
|
||||
"paidByRequired": "Du musst ein Mitglied auswählen.",
|
||||
"paidForMin1": "Die Ausgabe muss mindestens für ein Mitglied bezahlt werden.",
|
||||
"noZeroShares": "Alle Anteile müssen größer als 0 sein.",
|
||||
"amountSum": "Die Summe der Beträge muss dem Betrag der Ausgabe entsprechen.",
|
||||
"percentageSum": "Die Summe der prozentualen Anteile muss 100 ergeben.",
|
||||
"ratePositive": "Der Zinssatz muss unbedingt größer als Null sein."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Nach Kategorie suchen...",
|
||||
"noCategory": "Keine Kategorie gefunden.",
|
||||
"Uncategorized": {
|
||||
"heading": "Nicht kategorisiert",
|
||||
"General": "Allgemein",
|
||||
"Payment": "Zahlung"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Vergnügen",
|
||||
"Entertainment": "Vergnügen",
|
||||
"Games": "Spiele",
|
||||
"Movies": "Filme",
|
||||
"Music": "Musik",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Essen und Trinken",
|
||||
"Food and Drink": "Essen und Trinken",
|
||||
"Dining Out": "Essen gehen",
|
||||
"Groceries": "Lebensmittel",
|
||||
"Liquor": "Alkohol"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Zuhause",
|
||||
"Home": "Zuhause",
|
||||
"Electronics": "Elektronik",
|
||||
"Furniture": "Möbel",
|
||||
"Household Supplies": "Haushaltsgegenstände",
|
||||
"Maintenance": "Wartung",
|
||||
"Mortgage": "Hypothek",
|
||||
"Pets": "Haustiere",
|
||||
"Rent": "Miete",
|
||||
"Services": "Dienstleistungen"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Leben",
|
||||
"Childcare": "Kinderversorgung",
|
||||
"Clothing": "Kleidung",
|
||||
"Donation": "Spende",
|
||||
"Education": "Bildung",
|
||||
"Gifts": "Geschenke",
|
||||
"Insurance": "Versicherung",
|
||||
"Medical Expenses": "Medizinische Ausgaben",
|
||||
"Taxes": "Steuern"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transport",
|
||||
"Transportation": "Transport",
|
||||
"Bicycle": "Fahrrad",
|
||||
"Bus/Train": "Bus/Bahn",
|
||||
"Car": "Auto",
|
||||
"Gas/Fuel": "Tanken",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parken",
|
||||
"Plane": "Flugzeug",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Versorgung",
|
||||
"Utilities": "Versorgung",
|
||||
"Cleaning": "Reinigung/Putzen",
|
||||
"Electricity": "Strom",
|
||||
"Heat/Gas": "Heizung",
|
||||
"Trash": "Müll",
|
||||
"TV/Phone/Internet": "TV/Internet/Telefonie",
|
||||
"Water": "Wasser"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Währung suchen...",
|
||||
"noCurrency": "Keine Währungen gefunden.",
|
||||
"other": {
|
||||
"heading": "Andere Währungen"
|
||||
},
|
||||
"custom": {
|
||||
"heading": "Benutzerdefinierte"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Geläufigste"
|
||||
}
|
||||
}
|
||||
}
|
||||
452
messages/en-US.json
Normal file
@@ -0,0 +1,452 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Share <strong>Expenses</strong> with <strong>Friends & Family</strong>",
|
||||
"description": "Welcome to your new <strong>Spliit</strong> instance !",
|
||||
"button": {
|
||||
"groups": "Go to groups",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Groups"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Made in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Built by <author>Sebastien Castiel</author> and <source>contributors</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Expenses",
|
||||
"description": "Here are the expenses that you created for your group.",
|
||||
"create": "Create expense",
|
||||
"createFirst": "Create the first one",
|
||||
"noExpenses": "Your group doesn’t contain any expense yet.",
|
||||
"export": "Export",
|
||||
"exportJson": "Export to JSON",
|
||||
"exportCsv": "Export to CSV",
|
||||
"searchPlaceholder": "Search for an expense…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Who are you?",
|
||||
"description": "Tell us which participant you are to let us customize how the information is displayed.",
|
||||
"nobody": "I don’t want to select anyone",
|
||||
"save": "Save changes",
|
||||
"footer": "This setting can be changed later in the group settings."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Upcoming",
|
||||
"thisWeek": "This week",
|
||||
"earlierThisMonth": "Earlier this month",
|
||||
"lastMonth": "Last month",
|
||||
"earlierThisYear": "Earlier this year",
|
||||
"lastYear": "Last year",
|
||||
"older": "Older"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Paid by <strong>{paidBy}</strong> for <paidFor></paidFor>",
|
||||
"everyone": "everyone",
|
||||
"receivedBy": "Received by <strong>{paidBy}</strong> for <paidFor></paidFor>",
|
||||
"yourBalance": "Your balance:",
|
||||
"notInvolved": "You are not involved"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "My groups",
|
||||
"create": "Create",
|
||||
"loadingRecent": "Loading recent groups…",
|
||||
"NoRecent": {
|
||||
"description": "You have not visited any group recently.",
|
||||
"create": "Create one",
|
||||
"orAsk": "or ask a friend to send you the link to an existing one."
|
||||
},
|
||||
"recent": "Recent groups",
|
||||
"starred": "Starred groups",
|
||||
"archived": "Archived groups",
|
||||
"archive": "Archive group",
|
||||
"unarchive": "Unarchive group",
|
||||
"removeRecent": "Remove from recent groups",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Group has been removed",
|
||||
"description": "The group was removed from your recent groups list.",
|
||||
"undoAlt": "Undo group removal",
|
||||
"undo": "Undo"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Add by URL",
|
||||
"title": "Add a group by URL",
|
||||
"description": "If a group was shared with you, you can paste its URL here to add it to your list.",
|
||||
"error": "Oops, we are not able to find the group from the URL you provided…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "This group does not exist.",
|
||||
"link": "Go to recently visited groups"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Group information",
|
||||
"NameField": {
|
||||
"label": "Group name",
|
||||
"placeholder": "Summer vacations",
|
||||
"description": "Enter a name for your group."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Group information",
|
||||
"placeholder": "What information is relevant to group participants?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Currency symbol",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "We’ll use it to display amounts."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Main currency",
|
||||
"createDescription": "All amounts and balances will be in this currency.",
|
||||
"editDescription": "All amounts and balances will be in this currency. Changing this will NOT convert expenses already entered, except when the currency has different \"minor units\" than the current one (e.g. changing from US Dollar to Japanese Yen)",
|
||||
"customOption": "Custom"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participants",
|
||||
"description": "Enter the name for each participant.",
|
||||
"protectedParticipant": "This participant is part of expenses, and can not be removed.",
|
||||
"new": "New",
|
||||
"add": "Add participant",
|
||||
"John": "John",
|
||||
"Jane": "Jane",
|
||||
"Jack": "Jack"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Local settings",
|
||||
"description": "These settings are set per-device, and are used to customize your experience.",
|
||||
"ActiveUserField": {
|
||||
"label": "Active user",
|
||||
"placeholder": "Select a participant",
|
||||
"none": "None",
|
||||
"description": "User used as default for paying expenses."
|
||||
},
|
||||
"save": "Save",
|
||||
"saving": "Saving…",
|
||||
"create": "Create",
|
||||
"creating": "Creating…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Create income",
|
||||
"edit": "Edit income",
|
||||
"TitleField": {
|
||||
"label": "Income title",
|
||||
"placeholder": "Monday evening restaurant",
|
||||
"description": "Enter a description for the income."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Income date",
|
||||
"description": "Enter the date the income was received."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Currency of income",
|
||||
"description": "The currency in which the income was received."
|
||||
},
|
||||
"categoryFieldDescription": "Select the income category.",
|
||||
"paidByField": {
|
||||
"label": "Received by",
|
||||
"description": "Select the participant who received the income."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Received for",
|
||||
"description": "Select who the income was received for."
|
||||
},
|
||||
"splitModeDescription": "Select how to split the income.",
|
||||
"attachDescription": "See and attach receipts to the income."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Create expense",
|
||||
"edit": "Edit expense",
|
||||
"TitleField": {
|
||||
"label": "Expense title",
|
||||
"placeholder": "Monday evening restaurant",
|
||||
"description": "Enter a description for the expense."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Expense date",
|
||||
"description": "Enter the date the expense was paid."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Currency of expense",
|
||||
"description": "The currency in which the expense was paid."
|
||||
},
|
||||
"categoryFieldDescription": "Select the expense category.",
|
||||
"paidByField": {
|
||||
"label": "Paid by",
|
||||
"placeholder": "Select a participant",
|
||||
"description": "Select the participant who paid the expense."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Expense Recurrence",
|
||||
"description": "Select how often the expense should repeat.",
|
||||
|
||||
"none": "None",
|
||||
"daily": "Daily",
|
||||
"weekly": "Weekly",
|
||||
"monthly": "Monthly"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Paid for",
|
||||
"description": "Select who the expense was paid for."
|
||||
},
|
||||
"splitModeDescription": "Select how to split the expense.",
|
||||
"attachDescription": "See and attach receipts to the expense."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Amount"
|
||||
},
|
||||
"conversionUnavailable": "To set a different currency per expense and convert amounts, select a non-custom currency for the group.",
|
||||
"originalAmountField": {
|
||||
"label": "Amount to convert"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Use rates from Frankfurter",
|
||||
"useCustom": "Use custom rate",
|
||||
"label": "Exchange rate"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Getting exchange rates…",
|
||||
"success": "Obtained rates:",
|
||||
"error": "Oops, we could not get the most recent rates.",
|
||||
"staleRate": "Using rate:",
|
||||
"noRate": "Enter a custom rate below.",
|
||||
"currencyNotFound": "Oops, Frankfurter does not have the rate for this currency at this day.",
|
||||
"noDate": "Enter the expense date to get a conversion rate.",
|
||||
"dateMismatch": "Rates from date: {date}",
|
||||
"refresh": "Refresh",
|
||||
"customRate": "Using custom rate"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "This is a reimbursement"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Category"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notes"
|
||||
},
|
||||
"selectNone": "Select none",
|
||||
"selectAll": "Select all",
|
||||
"shares": "share(s)",
|
||||
"advancedOptions": "Advanced splitting options…",
|
||||
"SplitModeField": {
|
||||
"label": "Split mode",
|
||||
"evenly": "Evenly",
|
||||
"byShares": "Unevenly – By shares",
|
||||
"byPercentage": "Unevenly – By percentage",
|
||||
"byAmount": "Unevenly – By amount",
|
||||
"saveAsDefault": "Save as default splitting options"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Delete",
|
||||
"title": "Delete this expense?",
|
||||
"description": "Do you really want to delete this expense? This action is irreversible.",
|
||||
"yes": "Yes",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"attachDocuments": "Attach documents",
|
||||
"create": "Create",
|
||||
"creating": "Creating…",
|
||||
"save": "Save",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel",
|
||||
"reimbursement": "Reimbursement"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "The file is too big",
|
||||
"description": "The maximum file size you can upload is {maxSize}. Yours is {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error while uploading document",
|
||||
"description": "Something wrong happened when uploading the document. Please retry later or select a different file.",
|
||||
"retry": "Retry"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Create expense from receipt",
|
||||
"title": "Create from receipt",
|
||||
"description": "Extract the expense information from a receipt photo.",
|
||||
"body": "Upload the photo of a receipt, and we’ll scan it to extract the expense information if we can.",
|
||||
"selectImage": "Select image…",
|
||||
"titleLabel": "Title:",
|
||||
"categoryLabel": "Category:",
|
||||
"amountLabel": "Amount:",
|
||||
"dateLabel": "Date:",
|
||||
"editNext": "You’ll be able to edit the expense information next.",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"unknown": "Unknown",
|
||||
"TooBigToast": {
|
||||
"title": "The file is too big",
|
||||
"description": "The maximum file size you can upload is {maxSize}. Yours is {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error while uploading document",
|
||||
"description": "Something wrong happened when uploading the document. Please retry later or select a different file.",
|
||||
"retry": "Retry"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Balances",
|
||||
"description": "This is the amount that each participant paid or was paid for.",
|
||||
"Reimbursements": {
|
||||
"title": "Suggested reimbursements",
|
||||
"description": "Here are suggestions for optimized reimbursements between participants.",
|
||||
"noImbursements": "It looks like your group doesn’t need any reimbursement 😁",
|
||||
"owes": "<strong>{from}</strong> owes <strong>{to}</strong>",
|
||||
"markAsPaid": "Mark as paid"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Stats",
|
||||
"Totals": {
|
||||
"title": "Totals",
|
||||
"description": "Spending summary of the entire group.",
|
||||
"groupSpendings": "Total group spendings",
|
||||
"groupEarnings": "Total group earnings",
|
||||
"yourSpendings": "Your total spendings",
|
||||
"yourEarnings": "Your total earnings",
|
||||
"yourShare": "Your total share"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Activity",
|
||||
"description": "Overview of all activity in this group.",
|
||||
"noActivity": "There is not yet any activity in your group.",
|
||||
"someone": "Someone",
|
||||
"settingsModified": "Group settings were modified by <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Expense <em>{expense}</em> created by <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Expense <em>{expense}</em> updated by <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Expense <em>{expense}</em> deleted by <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Today",
|
||||
"yesterday": "Yesterday",
|
||||
"earlierThisWeek": "Earlier this week",
|
||||
"lastWeek": "Last week",
|
||||
"earlierThisMonth": "Earlier this month",
|
||||
"lastMonth": "Last month",
|
||||
"earlierThisYear": "Earlier this year",
|
||||
"lastYear": "Last year",
|
||||
"older": "Older"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Information",
|
||||
"description": "Use this place to add any information that can be relevant to the group participants.",
|
||||
"empty": "No group information yet."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Settings"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Share",
|
||||
"description": "For other participants to see the group and add expenses, share its URL with them.",
|
||||
"warning": "Warning!",
|
||||
"warningHelp": "Every person with the group URL will be able to see and edit expenses. Share with caution!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Enter at least one character.",
|
||||
"min2": "Enter at least two characters.",
|
||||
"max5": "Enter at most five characters.",
|
||||
"max50": "Enter at most 50 characters.",
|
||||
"duplicateParticipantName": "Another participant already has this name.",
|
||||
"titleRequired": "Please enter a title.",
|
||||
"invalidNumber": "Invalid number.",
|
||||
"amountRequired": "You must enter an amount.",
|
||||
"amountNotZero": "The amount must not be zero.",
|
||||
"amountTenMillion": "The amount must be lower than 10,000,000.",
|
||||
"ratePositive": "The rate must be strictly greater than zero.",
|
||||
"paidByRequired": "You must select a participant.",
|
||||
"paidForMin1": "The expense must be paid for at least one participant.",
|
||||
"noZeroShares": "All shares must be higher than 0.",
|
||||
"amountSum": "Sum of amounts must equal the expense amount.",
|
||||
"percentageSum": "Sum of percentages must equal 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Search category...",
|
||||
"noCategory": "No category found.",
|
||||
"Uncategorized": {
|
||||
"heading": "Uncategorized",
|
||||
"General": "General",
|
||||
"Payment": "Payment"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Entertainment",
|
||||
"Entertainment": "Entertainment",
|
||||
"Games": "Games",
|
||||
"Movies": "Movies",
|
||||
"Music": "Music",
|
||||
"Sports": "Sports"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Food and Drink",
|
||||
"Food and Drink": "Food and Drink",
|
||||
"Dining Out": "Dining Out",
|
||||
"Groceries": "Groceries",
|
||||
"Liquor": "Liquor"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Home",
|
||||
"Home": "Home",
|
||||
"Electronics": "Electronics",
|
||||
"Furniture": "Furniture",
|
||||
"Household Supplies": "Household Supplies",
|
||||
"Maintenance": "Maintenance",
|
||||
"Mortgage": "Mortgage",
|
||||
"Pets": "Pets",
|
||||
"Rent": "Rent",
|
||||
"Services": "Services"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Life",
|
||||
"Childcare": "Childcare",
|
||||
"Clothing": "Clothing",
|
||||
"Donation": "Donation",
|
||||
"Education": "Education",
|
||||
"Gifts": "Gifts",
|
||||
"Insurance": "Insurance",
|
||||
"Medical Expenses": "Medical Expenses",
|
||||
"Taxes": "Taxes"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transportation",
|
||||
"Transportation": "Transportation",
|
||||
"Bicycle": "Bicycle",
|
||||
"Bus/Train": "Bus/Train",
|
||||
"Car": "Car",
|
||||
"Gas/Fuel": "Gas/Fuel",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parking",
|
||||
"Plane": "Plane",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilities",
|
||||
"Utilities": "Utilities",
|
||||
"Cleaning": "Cleaning",
|
||||
"Electricity": "Electricity",
|
||||
"Heat/Gas": "Heat/Gas",
|
||||
"Trash": "Trash",
|
||||
"TV/Phone/Internet": "TV/Phone/Internet",
|
||||
"Water": "Water"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Search currency...",
|
||||
"noCurrency": "No currencies found.",
|
||||
"custom": {
|
||||
"heading": "Custom"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Most common"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Other currencies"
|
||||
}
|
||||
}
|
||||
}
|
||||
451
messages/es.json
Normal file
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Comparte <strong>Gastos</strong> con <strong>Amigos y Familia</strong>",
|
||||
"description": "¡Bienvenido a tu nueva instancia de <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Ir a grupos",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grupos"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Hecho en Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Construido por <author>Sebastien Castiel</author> y <source>colaboradores</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Gastos",
|
||||
"description": "Aqui encontraras los gastos que has creado para tu grupo.",
|
||||
"create": "Crear gasto",
|
||||
"createFirst": "Crea el primero",
|
||||
"noExpenses": "Tu grupo aun no tiene gastos.",
|
||||
"export": "Exportar",
|
||||
"exportJson": "Exportar a JSON",
|
||||
"exportCsv": "Exportar a CSV",
|
||||
"searchPlaceholder": "Busca un gasto…",
|
||||
"ActiveUserModal": {
|
||||
"title": "¿Quién es usted?",
|
||||
"description": "Dinos qué participante eres para que podamos personalizar cómo se muestra la información.",
|
||||
"nobody": "No quiero seleccionar a nadie",
|
||||
"save": "Guardar cambios",
|
||||
"footer": "Esta configuración puede modificarse posteriormente en los ajustes del grupo."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Próximamente",
|
||||
"thisWeek": "Esta semana",
|
||||
"earlierThisMonth": "A principios de este mes",
|
||||
"lastMonth": "El mes pasado",
|
||||
"earlierThisYear": "A principios de este año",
|
||||
"lastYear": "El año pasado",
|
||||
"older": "Más antiguo"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Pagado por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"receivedBy": "Recibido por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"yourBalance": "Tu balance:",
|
||||
"everyone": "todos",
|
||||
"notInvolved": "No estás incluido"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Mis grupos",
|
||||
"create": "Crear",
|
||||
"loadingRecent": "Cargando grupos recientes…",
|
||||
"NoRecent": {
|
||||
"description": "No has visitado ningun grupo recientemente.",
|
||||
"create": "Crea uno",
|
||||
"orAsk": "o pídele a un amigo que te envíe el enlace a uno ya existente."
|
||||
},
|
||||
"recent": "Grupos recientes",
|
||||
"starred": "Grupos favoritos",
|
||||
"archived": "Grupos archivados",
|
||||
"archive": "Archivar grupo",
|
||||
"unarchive": "Desarchivar groupo",
|
||||
"removeRecent": "Eliminar de grupos recientes",
|
||||
"RecentRemovedToast": {
|
||||
"title": "El grupo ha sido eliminado",
|
||||
"description": "El grupo ha sido eliminado de tu lista de grupos recientes.",
|
||||
"undoAlt": "Deshacer la eliminación del grupo",
|
||||
"undo": "Deshacer"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Añadir mediante url",
|
||||
"title": "Añadir grupo mediante url",
|
||||
"description": "Si un grupo ha sido compartido contigo, puedes pegar su URL aquí para añadirlo a tu lista.",
|
||||
"error": "Ups, no pudimos encontrar el grupo a partir de la URL que proporcionaste…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Este grupo no existe.",
|
||||
"link": "Ir a los grupos visitados recientemente"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Información del grupo",
|
||||
"NameField": {
|
||||
"label": "Nombre del grupo",
|
||||
"placeholder": "Vacaciones de verano",
|
||||
"description": "Introduce un nombre para tu grupo."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Información del grupo",
|
||||
"placeholder": "¿Qué información es relevante para los participantes del grupo?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Símbolo de la divisa",
|
||||
"placeholder": "$, €, £, ₿…",
|
||||
"description": "Lo utilizaremos para mostrar los montos."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participantes",
|
||||
"description": "Ingresa el nombre de cada participante.",
|
||||
"protectedParticipant": "Estos participantes forman parte de gastos y no pueden ser eliminados.",
|
||||
"new": "Nuevo",
|
||||
"add": "Añadir participante",
|
||||
"John": "Juan",
|
||||
"Jane": "Maria",
|
||||
"Jack": "Sergio"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ajustes locales",
|
||||
"description": "Estos ajustes se establecen por dispositivo y se utilizan para personalizar su experiencia.",
|
||||
"ActiveUserField": {
|
||||
"label": "Usuario activo",
|
||||
"placeholder": "Selecciona un participante",
|
||||
"none": "Ninguno",
|
||||
"description": "Usuario que paga los gastos por defecto."
|
||||
},
|
||||
"save": "Guardar",
|
||||
"saving": "Guardando…",
|
||||
"create": "Crear",
|
||||
"creating": "Creando…",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Moneda principal",
|
||||
"createDescription": "Todos los importes y saldos estarán en esta moneda.",
|
||||
"editDescription": "Todos los importes y saldos estarán en esta moneda. Al cambiarla, NO se convertirán los gastos ya ingresados, excepto cuando la moneda tenga «unidades menores» diferentes a las actuales (por ejemplo, al cambiar de dólares estadounidenses a yenes japoneses)",
|
||||
"customOption": "Personalizado"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Crear ingreso",
|
||||
"edit": "Editar ingreso",
|
||||
"TitleField": {
|
||||
"label": "Título del ingreso",
|
||||
"placeholder": "Comida Hamburgeseria",
|
||||
"description": "Introduce una descripción para este ingreso."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Fecha del ingreso",
|
||||
"description": "Ingresa la fecha en que se recibio el ingreso."
|
||||
},
|
||||
"categoryFieldDescription": "Seleccione la categoría de ingresos.",
|
||||
"paidByField": {
|
||||
"label": "Recibido por",
|
||||
"description": "Seleccione el participante que recibió los ingresos."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Recibido para for",
|
||||
"description": "Seleccione para quién se recibió el ingreso."
|
||||
},
|
||||
"splitModeDescription": "Seleccione como quieres dividir el ingreso.",
|
||||
"attachDescription": "Ver y adjuntar tickets para el ingreso.",
|
||||
"currencyField": {
|
||||
"label": "Moneda del ingreso",
|
||||
"description": "La moneda en la que se recibieron los ingresos."
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Crear gasto",
|
||||
"edit": "Editar gasto",
|
||||
"TitleField": {
|
||||
"label": "Título del gasto",
|
||||
"placeholder": "Restaurante de lunes por la noche",
|
||||
"description": "Ingrese una descripción del gasto."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Fecha del gasto",
|
||||
"description": "Ingresa la fecha en que se recibio el gasto."
|
||||
},
|
||||
"categoryFieldDescription": "Seleccione la categoría del gasto.",
|
||||
"paidByField": {
|
||||
"label": "Pagado por",
|
||||
"description": "Seleccione el participante que pagó el gasto.",
|
||||
"placeholder": "Seleccionar un participante"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Gasto recurrente",
|
||||
"description": "Seleccione con qué frecuencia debe repetirse el gasto.",
|
||||
"none": "Ninguno",
|
||||
"daily": "Diario",
|
||||
"weekly": "Semanal",
|
||||
"monthly": "Mensual"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Pagado para",
|
||||
"description": "Seleccione para quién se pagó el gasto."
|
||||
},
|
||||
"splitModeDescription": "Seleccione como quieres dividir el gasto.",
|
||||
"attachDescription": "Ver y adjuntar tickets para el gasto.",
|
||||
"currencyField": {
|
||||
"label": "Moneda del gasto",
|
||||
"description": "La moneda en la que se pagó el gasto."
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Cantidad"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Esto es un reembolso"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notas"
|
||||
},
|
||||
"selectNone": "Seleccionar ninguno",
|
||||
"selectAll": "Seleccionar todos",
|
||||
"shares": "partes",
|
||||
"advancedOptions": "Opciones avanzadas de división…",
|
||||
"SplitModeField": {
|
||||
"label": "Modo de división",
|
||||
"evenly": "Uniformemente",
|
||||
"byShares": "Desigualmente – Por partes",
|
||||
"byPercentage": "Desigualmente – por porcentaje",
|
||||
"byAmount": "Desigualmente – por cantidad",
|
||||
"saveAsDefault": "Guardar como modo preferido"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Borrar",
|
||||
"title": "Borrar gasto?",
|
||||
"description": "Seguro que quieres borrar este gasto? Esta acción es irreversible.",
|
||||
"yes": "Si",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"attachDocuments": "Adjuntar documentos",
|
||||
"create": "Crear",
|
||||
"creating": "Creando…",
|
||||
"save": "Guardar",
|
||||
"saving": "Guardando…",
|
||||
"cancel": "Cancelar",
|
||||
"reimbursement": "Reembolso",
|
||||
"conversionUnavailable": "Para establecer una moneda diferente por gasto y convertir los importes, seleccione una moneda no personalizada para el grupo.",
|
||||
"originalAmountField": {
|
||||
"label": "Monto a convertir"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Utilizar las tasas del Frankfurter",
|
||||
"useCustom": "Utilizar tasa personalizada",
|
||||
"label": "Tasa de cambio"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Obteniendo tasas de cambio…",
|
||||
"success": "Tasas obtenidas:",
|
||||
"error": "Vaya, no hemos podido obtener las tasas más recientes.",
|
||||
"staleRate": "Tasa utilizada:",
|
||||
"noRate": "Ingrese una tasa personalizada.",
|
||||
"currencyNotFound": "Vaya, Frankfurter no tiene el tipo de cambio para esta moneda en este día.",
|
||||
"noDate": "Ingrese la fecha del gasto para obtener una tasa de cambio.",
|
||||
"dateMismatch": "Tasas para la fecha: {date}",
|
||||
"refresh": "Actualizar",
|
||||
"customRate": "Utilizando tasa personalizada"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "El archivo es demasiado grande",
|
||||
"description": "El tamaño máximo de archivo que puede cargar es {maxSize}. El tuyo pesa {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error al cargar el documento",
|
||||
"description": "Ha ocurrido un error al cargar el documento. Vuelva a intentarlo más tarde o seleccione otro archivo.",
|
||||
"retry": "Reintentar"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Crear gasto desde ticket",
|
||||
"title": "Crear desde ticket",
|
||||
"description": "Extraer la información de gastos de una foto de recibo.",
|
||||
"body": "Sube la foto de un recibo y lo escanearemos para extraer la información del gasto si podemos.",
|
||||
"selectImage": "Seleccionar imagen…",
|
||||
"titleLabel": "Titulo:",
|
||||
"categoryLabel": "Categoria:",
|
||||
"amountLabel": "Cantidad:",
|
||||
"dateLabel": "Fecha:",
|
||||
"editNext": "A continuación podrá editar la información de los gastos.",
|
||||
"continue": "Continuar"
|
||||
},
|
||||
"unknown": "Desconocido",
|
||||
"TooBigToast": {
|
||||
"title": "El archivo es demasiado grande",
|
||||
"description": "El tamaño máximo de archivo que puede cargar es {maxSize}. El tuyo pesa {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error al cargar el documento",
|
||||
"description": "Ha ocurrido un error al cargar el documento. Vuelva a intentarlo más tarde o seleccione otro archivo.",
|
||||
"retry": "Reintentar"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Balances",
|
||||
"description": "Se trata del importe que ha pagado o ha recibido cada participante.",
|
||||
"Reimbursements": {
|
||||
"title": "Reembolsos propuestos",
|
||||
"description": "He aquí algunas sugerencias para optimizar los reembolsos entre los participantes.",
|
||||
"noImbursements": "Parece que tu grupo no necesita ningún reembolso 😁",
|
||||
"owes": "<strong>{from}</strong> debe a <strong>{to}</strong>",
|
||||
"markAsPaid": "Marcar como pagado"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Estadísticas",
|
||||
"Totals": {
|
||||
"title": "Totales",
|
||||
"description": "Resumen de gastos de todo el grupo.",
|
||||
"groupSpendings": "Gastos de todo el grupo",
|
||||
"groupEarnings": "Ingresos de todo el grupo",
|
||||
"yourSpendings": "Tus gastos totales",
|
||||
"yourEarnings": "Tus ingresos totales",
|
||||
"yourShare": "Tu parte final"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Actividad",
|
||||
"description": "Aquí encontrarás todas las actividades recientes en tu grupo.",
|
||||
"noActivity": "No hay actividad reciente en este grupo.",
|
||||
"someone": "Alguien",
|
||||
"settingsModified": "Los ajustes del grupo fueron modificados por <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Gasto <em>{expense}</em> creado por <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Gasto <em>{expense}</em> actualizado por <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Gasto <em>{expense}</em> borrado por <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Hoy",
|
||||
"yesterday": "Ayer",
|
||||
"earlierThisWeek": "A principios de esta semana",
|
||||
"lastWeek": "La semana pasada",
|
||||
"earlierThisMonth": "A principios de este mes",
|
||||
"lastMonth": "El mes pasado",
|
||||
"earlierThisYear": "A principios de este año",
|
||||
"lastYear": "El ultimo año",
|
||||
"older": "Más antiguos"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Información",
|
||||
"description": "Utilice este lugar para añadir cualquier información que pueda ser relevante para los participantes del grupo.",
|
||||
"empty": "Aún no hay información sobre el grupo."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ajustes"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Compartir",
|
||||
"description": "Para que otros participantes puedan ver el grupo y añadir gastos, compárteles su URL.",
|
||||
"warning": "Cuidado!",
|
||||
"warningHelp": "Todas las personas que tengan la URL del grupo podrán ver y editar los gastos. ¡Comparte con precaución!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Introduzca al menos un carácter.",
|
||||
"min2": "Introduzca al menos dos carácter.",
|
||||
"max5": "Introduzca al menos cinco carácter.",
|
||||
"max50": "Introduzca al menos treinta carácter.",
|
||||
"duplicateParticipantName": "Ya hay otro participante con el mismo nombre.",
|
||||
"titleRequired": "Por favor, introduzca un título.",
|
||||
"invalidNumber": "Número inválido.",
|
||||
"amountRequired": "Debe introducir un importe.",
|
||||
"amountNotZero": "El importe no debe ser cero.",
|
||||
"amountTenMillion": "El importe debe ser inferior a 10.000.000.",
|
||||
"paidByRequired": "Debe seleccionar un participante.",
|
||||
"paidForMin1": "El gasto debe ser pagado por al menos un participante.",
|
||||
"noZeroShares": "Todas las partes deben ser mayor que 0.",
|
||||
"amountSum": "La suma de los importes debe ser igual al importe del gasto total.",
|
||||
"percentageSum": "Suma de porcentajes debe ser igual a 100.",
|
||||
"ratePositive": "La tasa debe ser mayor a cero."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Buscar categoría...",
|
||||
"noCategory": "Categoría no encontrada.",
|
||||
"Uncategorized": {
|
||||
"heading": "Sin categoría",
|
||||
"General": "General",
|
||||
"Payment": "Pago"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Ocio",
|
||||
"Entertainment": "Ocio",
|
||||
"Games": "Juegos",
|
||||
"Movies": "Películas",
|
||||
"Music": "Musica",
|
||||
"Sports": "Deportes"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Comida y bebida",
|
||||
"Food and Drink": "Comida y bebida",
|
||||
"Dining Out": "Comer fuera",
|
||||
"Groceries": "Comestibles",
|
||||
"Liquor": "Licores"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Hogar",
|
||||
"Home": "Hogar",
|
||||
"Electronics": "Electrónica",
|
||||
"Furniture": "Muebles",
|
||||
"Household Supplies": "Suministros del hogar",
|
||||
"Maintenance": "Mantenimiento",
|
||||
"Mortgage": "Hipoteca",
|
||||
"Pets": "Mascotas",
|
||||
"Rent": "Alquiler",
|
||||
"Services": "Servicios"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Vida",
|
||||
"Childcare": "Cuidado de niños",
|
||||
"Clothing": "Ropa",
|
||||
"Education": "Educación",
|
||||
"Gifts": "Regalos",
|
||||
"Insurance": "Seguro",
|
||||
"Medical Expenses": "Gastos médicos",
|
||||
"Taxes": "Impuestos",
|
||||
"Donation": "Donación"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transporte",
|
||||
"Transportation": "Transporte",
|
||||
"Bicycle": "Bicicleta",
|
||||
"Bus/Train": "Autobús/Tren",
|
||||
"Car": "Coche",
|
||||
"Gas/Fuel": "Gasolina/Combustible",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parking",
|
||||
"Plane": "Avión",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilidades",
|
||||
"Utilities": "Utilidades",
|
||||
"Cleaning": "Limpieza",
|
||||
"Electricity": "Electricidad",
|
||||
"Heat/Gas": "Calefacción/Gas",
|
||||
"Trash": "Basura",
|
||||
"TV/Phone/Internet": "TV/Teléfono/Internet",
|
||||
"Water": "Agua"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Buscar moneda...",
|
||||
"noCurrency": "No se han podido encontrar monedas.",
|
||||
"custom": {
|
||||
"heading": "Personalizado"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Más común"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Otras monedas"
|
||||
}
|
||||
}
|
||||
}
|
||||
451
messages/eu.json
Normal file
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Partekatu <strong>gastuak lagunekin eta familiarekin</strong>",
|
||||
"description": "Ongi etorri zure <strong>Spliit</strong> intantzia berrira!",
|
||||
"button": {
|
||||
"groups": "Joan taldeetara",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Taldeak"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Montrealen egina, Quebec 🇨🇦",
|
||||
"builtBy": "<author>Sebastien Castiel</author> eta <source>laguntzaileek</source> egina"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Gastuak",
|
||||
"description": "Hemen talde honetan sortutako gastuak aurkituko dituzu.",
|
||||
"create": "Sortu gastua",
|
||||
"createFirst": "Sortu lehenengoa",
|
||||
"noExpenses": "Talde honek oraindik ez du gasturik.",
|
||||
"export": "Esportatu",
|
||||
"exportJson": "Esportatu JSONera",
|
||||
"Groups": {
|
||||
"earlierThisYear": "Urte hasieran",
|
||||
"lastYear": "Iaz",
|
||||
"older": "Zaharrena",
|
||||
"upcoming": "Laster",
|
||||
"thisWeek": "Aste honetan",
|
||||
"earlierThisMonth": "Hilabete hasieran",
|
||||
"lastMonth": "Aurreko hilabetean"
|
||||
},
|
||||
"exportCsv": "Esportatu CSVra",
|
||||
"searchPlaceholder": "Bilatu gastu bat…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Nor zara?",
|
||||
"description": "Esan zein parte-hartzaile zaren, informazioa nola bistaratzen den pertsonalizatzeko.",
|
||||
"nobody": "Ez dut inor aukeratu nahi",
|
||||
"save": "Gorde aldaketak",
|
||||
"footer": "Doikuntza hori geroago alda daiteke taldearen konfigurazioan."
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong>-k ordaindua <paidFor></paidFor>-rentzat",
|
||||
"everyone": "denak",
|
||||
"receivedBy": "<strong>{paidBy}</strong>-k jasota <paidFor></paidFor>-rentzat",
|
||||
"yourBalance": "Zure saldoa:",
|
||||
"notInvolved": "Ez duzu parte hartzen"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Nire taldeak",
|
||||
"create": "Sortu",
|
||||
"loadingRecent": "Azken taldeak kargatzen…",
|
||||
"NoRecent": {
|
||||
"description": "Azken aldian ez duzu talderik bisitatu.",
|
||||
"create": "Sortu bat",
|
||||
"orAsk": "edo eskatu lagun bati lehendik dagoen baten esteka bidaltzeko."
|
||||
},
|
||||
"recent": "Azken taldeak",
|
||||
"starred": "Gogoko taldeak",
|
||||
"archived": "Gordetako taldeak",
|
||||
"archive": "Gorde taldea",
|
||||
"unarchive": "Kendu taldea artxibotik",
|
||||
"removeRecent": "Kendu azken taldeetatik",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Taldea ezabatu da",
|
||||
"description": "Taldea azken taldeen zerrendatik ezabatu da.",
|
||||
"undoAlt": "Desegin taldearen ezabatzea",
|
||||
"undo": "Desegin"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Gehitu URL bidez",
|
||||
"title": "Gehitu talde bat URL bidez",
|
||||
"description": "Talde bat zurekin partekatu bada, hemen itsatsi dezakezu bere URLa zure zerrendara gehitzeko.",
|
||||
"error": "Oops, ezin dugu taldea aurkitu zuk emandako URLtik…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Talde hori ez da existitzen.",
|
||||
"link": "Joan bisitatutako azken taldeetara"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Taldeko informazioa",
|
||||
"NameField": {
|
||||
"label": "Taldearen izena",
|
||||
"placeholder": "Udako oporrak",
|
||||
"description": "Eman izena zure taldeari."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Taldeko informazioa",
|
||||
"placeholder": "Zer informazio da garrantzitsua taldeko parte-hartzaileentzat?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Monetaren ikurra",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Zenbatekoak erakusteko erabiliko dugu."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Moneta nagusia",
|
||||
"createDescription": "Zenbatekoak eta saldoak monetan honetan adieraziko dira.",
|
||||
"editDescription": "Zenbateko eta saldo guztiak moneta honetan adieraziko dira. Hori aldatuz gero, EZ dira bihurtuko lehendik sartutako gastuak, dibisak egungoaren \"unitate txiki\" desberdinak dituenean izan ezik (adib. AEBko dolarretik Japoniako yenera aldatuz)",
|
||||
"customOption": "Pertsonalizatua"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Kideak",
|
||||
"description": "Sartu kide bakoitzaren izena.",
|
||||
"protectedParticipant": "Kide hauek gastuak dituzte eta ezin dira ezabatu.",
|
||||
"new": "Berria",
|
||||
"add": "Gehitu kidea",
|
||||
"John": "Jon",
|
||||
"Jane": "Jone",
|
||||
"Jack": "Santi"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ezarpen lokalak",
|
||||
"description": "Ezarpen hauek gailuari lotuta daude eta zure esperientzia pertsonalizatzeko erabiltzen dira.",
|
||||
"ActiveUserField": {
|
||||
"label": "Erabiltzaile aktiboa",
|
||||
"placeholder": "Hautatu kide bat",
|
||||
"none": "Bat ere ez",
|
||||
"description": "Gastuak ordaintzeko lehenetsi gisa erabiltzen den erabiltzailea."
|
||||
},
|
||||
"save": "Gorde",
|
||||
"saving": "Gordetzen…",
|
||||
"create": "Sortu",
|
||||
"creating": "Sortzen…",
|
||||
"cancel": "Utzi"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Sortu diru-sarrera",
|
||||
"edit": "Editatu diru-sarrera",
|
||||
"TitleField": {
|
||||
"label": "Diru-sarreraren izena",
|
||||
"placeholder": "Astelehenen arratseko jatetxea",
|
||||
"description": "Sartu diru-sarreraren deskripzioa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Diru-sarreraren data",
|
||||
"description": "Sartu diru-sarrera jaso den data."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Diru-sarreraren moneta",
|
||||
"description": "Diru-sarrerak zein monetan jaso diren."
|
||||
},
|
||||
"categoryFieldDescription": "Hautatu diru-sarreraren kategoria.",
|
||||
"paidByField": {
|
||||
"label": "Zeinek jaso du",
|
||||
"description": "Hautatu zein kidek jaso duen diru-sarrera."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Nork jaso du",
|
||||
"description": "Hautatu norentzat jaso den diru-sarrera."
|
||||
},
|
||||
"splitModeDescription": "Hautatu nola zatitu diru-sarrera.",
|
||||
"attachDescription": "Ordainagiriak ikusi eta diru-sarrerei erantsi."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Sortu gastua",
|
||||
"edit": "Editatu gastua",
|
||||
"TitleField": {
|
||||
"label": "Gastuaren izena",
|
||||
"placeholder": "Astelehen arratseko jatetxea",
|
||||
"description": "Sartu gastuaren deskripzioa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Gastuaren data",
|
||||
"description": "Sartu gastua ordaindu deneko data."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Gastuaren moneta",
|
||||
"description": "Gastua zein monetatan ordaindu den."
|
||||
},
|
||||
"categoryFieldDescription": "Hautatu gastuaren kategoria.",
|
||||
"paidByField": {
|
||||
"label": "Zeinek ordaindu du",
|
||||
"placeholder": "Hautatu kide bat",
|
||||
"description": "Hautatu gastua ordaindu duen kidea."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Gastu errepikakorra",
|
||||
"description": "Hautatu gastua zein maiztasunez errepikatu behar den.",
|
||||
"none": "Bat ere ez",
|
||||
"daily": "Egunero",
|
||||
"weekly": "Astero",
|
||||
"monthly": "Hilabetero"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Honentzat ordaindu da",
|
||||
"description": "Hautatu gastua norentzat ordaindu den."
|
||||
},
|
||||
"splitModeDescription": "Hautatu nola zatitu gastua.",
|
||||
"attachDescription": "Ikusi eta erantsi gastuaren ordainagiriak."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Zenbatekoa"
|
||||
},
|
||||
"conversionUnavailable": "Gastu bakoitzeko moneta bat ezartzeko eta zenbatekoak bihurtzeko, hautatu taldearentzako moneta ez-pertsonalizatu bat.",
|
||||
"originalAmountField": {
|
||||
"label": "Bihurtzeko zenbatekoa"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Erabili Frankfurter-en tasak",
|
||||
"useCustom": "Erabili tasa pertsonalizatua",
|
||||
"label": "Kanbio-tasa"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Kanbio-tasak eskuratzen…",
|
||||
"success": "Eskuratutako tasak:",
|
||||
"error": "Oops, ezin izan ditugu azken tasak lortu.",
|
||||
"staleRate": "Erabilitako tasa:",
|
||||
"noRate": "Sartu tarifa pertsonalizatua behean.",
|
||||
"currencyNotFound": "Oops, Frankfurterrek ez du moneta honen tasa gaur egun.",
|
||||
"noDate": "Sartu gastuaren data bihurketa-tasa lortzeko.",
|
||||
"dateMismatch": "Tarifak datarako: {date}",
|
||||
"refresh": "Eguneratu",
|
||||
"customRate": "Tasa pertsonalizatua erabiltzen"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Hau diru-itzultzea da"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Oharrak"
|
||||
},
|
||||
"selectNone": "Hautatu bat ere ez",
|
||||
"selectAll": "Hautatu denak",
|
||||
"shares": "partekatzea(k)",
|
||||
"advancedOptions": "Zatitzeko aukera aurreratuak…",
|
||||
"SplitModeField": {
|
||||
"label": "Zatitzeko modua",
|
||||
"evenly": "Zati berdinetan",
|
||||
"byShares": "Zati ezberdinetan - Partekatzetan",
|
||||
"byPercentage": "Zati ezberdinetan - ehunekoen bidez",
|
||||
"byAmount": "Zati desberdinetan - zenbatekoen bidez",
|
||||
"saveAsDefault": "Gorde gogoko zatitze modu gisa"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Ezabatu",
|
||||
"title": "Ezabatu gastu hau?",
|
||||
"description": "Benetan gastu hau ezabatu nahi duzu? Ekintzak ez dauka atzera bueltarik.",
|
||||
"yes": "Bai",
|
||||
"cancel": "Utzi"
|
||||
},
|
||||
"attachDocuments": "Erantsi dokumentuak",
|
||||
"create": "Sortu",
|
||||
"creating": "Sortzen…",
|
||||
"save": "Gorde",
|
||||
"saving": "Gordetzen…",
|
||||
"cancel": "Utzi",
|
||||
"reimbursement": "Diru-itzultzea"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Fitxategia handiegia da",
|
||||
"description": "Igo dezakezun fitxategirik handiena {maxSize} da. Zureak {size} pisatzen du."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Errorea dokumentua igotzean",
|
||||
"description": "Errore bat gertatu da dokumentua igotzean. Saiatu geroago edo hautatu beste fitxategi bat.",
|
||||
"retry": "Saiatu berriro"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Sortu gastua ordainagiri batetik",
|
||||
"title": "Sortu ordainagiritik",
|
||||
"description": "Erauzi gastuaren informazioa ordainagiriren argazkitik.",
|
||||
"body": "Igo ordainagiri baten argazkia eta eskaneatuko dugu gastuaren informazioa erauzteko, ahal badugu.",
|
||||
"selectImage": "Hautatu irudia…",
|
||||
"titleLabel": "Titulua:",
|
||||
"categoryLabel": "Kategoria:",
|
||||
"amountLabel": "Zenbatekoa:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "Ondoren, gastuaren informazioa editatzeko aukera izango duzu.",
|
||||
"continue": "Jarraitu"
|
||||
},
|
||||
"unknown": "Ezezaguna",
|
||||
"TooBigToast": {
|
||||
"title": "Fitxategia handiegia da",
|
||||
"description": "Igo dezakezun fitxategiaren gehienezko tamaina {maxSize} da. Zureak {size} pisatzen du."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Errorea dokumentua igotzean",
|
||||
"description": "Dokumentua igotzean errore bat gertatu da. Saiatu berriro beranduago edo hautatu beste fitxategi bat.",
|
||||
"retry": "Saiatu berriro"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Saldoak",
|
||||
"description": "Hau da kide bakoitzak ordaindu edo jaso duen zenbatekoa.",
|
||||
"Reimbursements": {
|
||||
"title": "Proposatutako diru-itzulketak",
|
||||
"description": "Hona hemen kideen arteko ditu-itzulketak optimizatzeko proposamenak.",
|
||||
"noImbursements": "Antza denez zure taldeak ez du diru-itzulketarik behar 😁",
|
||||
"owes": "<strong>{from}</strong> zor dio <strong>{to}</strong>-ri",
|
||||
"markAsPaid": "Markatu ordainduta gisa"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Estatistikak",
|
||||
"Totals": {
|
||||
"title": "Denetara",
|
||||
"description": "Talde osoaren gastuen laburpena.",
|
||||
"groupSpendings": "Talde osoaren gastuak",
|
||||
"groupEarnings": "Talde osoaren diru-sarrerak",
|
||||
"yourSpendings": "Zure gastuak denetara",
|
||||
"yourEarnings": "Zure diru-sarrerak denetara",
|
||||
"yourShare": "Zure parte osoa"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Jarduera",
|
||||
"description": "Talde honetako jarduera guztien ikuspegi orokorra.",
|
||||
"noActivity": "Oraindik ez dago jarduerarik zure taldean.",
|
||||
"someone": "Norbait",
|
||||
"settingsModified": "<strong>{participant}</strong>-k taldearen ezarpenak aldatu ditu.",
|
||||
"expenseCreated": "<em>{expense}</em>ko gastua sortu du <strong>{participant}</strong>-k.",
|
||||
"expenseUpdated": "<em>{expense}</em>ko gastua <strong>{participant}</strong>-k sortua.",
|
||||
"expenseDeleted": "<em>{expense}</em>ko gastua ezabatu du <strong>{participant}</strong>-k.",
|
||||
"Groups": {
|
||||
"today": "Gaur",
|
||||
"yesterday": "Atzo",
|
||||
"earlierThisWeek": "Aste honen hasieran",
|
||||
"lastWeek": "Pasa den astean",
|
||||
"earlierThisMonth": "Hilabete honen hasieran",
|
||||
"lastMonth": "Pasa den hilabetean",
|
||||
"earlierThisYear": "Urte honen hasieran",
|
||||
"lastYear": "Iaz",
|
||||
"older": "Zaharragoak"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informazioa",
|
||||
"description": "Erabili leku hau taldeko kideentzat garrantzitsua izan daitekeen edozein informazio gehitzeko.",
|
||||
"empty": "Oraindik ez dago taldearen informaziorik."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ezarpenak"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Partekatu",
|
||||
"description": "Beste kide batzuek taldea ikusteko eta gastuak gehitzeko, partekatu haiekin URLa.",
|
||||
"warning": "Kontuz!",
|
||||
"warningHelp": "Taldeko URLa duen edozein pertsonak gastuak ikusi eta editatu ahal izango ditu. Partekatu kontuz!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Sartu gutxienez karaktere bat.",
|
||||
"min2": "Sartu gutxienez bi karaktere.",
|
||||
"max5": "Sartu gutxienez bost karaktere.",
|
||||
"max50": "Sartu gutxienez 50 karaktere.",
|
||||
"duplicateParticipantName": "Badago izen hori duen beste kide bat.",
|
||||
"titleRequired": "Sartu titulu bat.",
|
||||
"invalidNumber": "Zenbaki baliogabea.",
|
||||
"amountRequired": "Zenbateko bat sartu behar duzu.",
|
||||
"amountNotZero": "Zenbatekoa ezin da zero izan.",
|
||||
"amountTenMillion": "Zenbatekoa 10.000.000 baino txikiagoa izan behar du.",
|
||||
"ratePositive": "Tasa zero baino handiagoa izan behar du.",
|
||||
"paidByRequired": "Kide bat hautatu behar duzu.",
|
||||
"paidForMin1": "Gastua gutxienez kide batek ordaindu behar du.",
|
||||
"noZeroShares": "Parte guztiek zero baino handiagoak izan behar dute.",
|
||||
"amountSum": "Zenbatekoen baturak gastuaren zenbatekoa berdindu behar du.",
|
||||
"percentageSum": "Ehunekoen baturak 100 izan behar du."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Bilatu kategoria...",
|
||||
"noCategory": "Ez da kategoriarik aurkitu.",
|
||||
"Uncategorized": {
|
||||
"heading": "Kategoriarik gabe",
|
||||
"General": "Orokorra",
|
||||
"Payment": "Ordainketa"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Aisia",
|
||||
"Entertainment": "Aisia",
|
||||
"Games": "Jolasak",
|
||||
"Movies": "Filmak",
|
||||
"Music": "Musika",
|
||||
"Sports": "Kirolak"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Janari-edariak",
|
||||
"Food and Drink": "Janari-edariak",
|
||||
"Dining Out": "Kanpoan afaltzea",
|
||||
"Groceries": "Janariak",
|
||||
"Liquor": "Likoreak"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Etxea",
|
||||
"Home": "Etxea",
|
||||
"Electronics": "Elektronika",
|
||||
"Furniture": "Altzariak",
|
||||
"Household Supplies": "Etxeko hornidurak",
|
||||
"Maintenance": "Mantenua",
|
||||
"Mortgage": "Hipoteka",
|
||||
"Pets": "Maskotak",
|
||||
"Rent": "Etxeko errenta",
|
||||
"Services": "Zerbitzuak"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Bizitza",
|
||||
"Childcare": "Umeen zaintza",
|
||||
"Clothing": "Arropa",
|
||||
"Donation": "Dohaintza",
|
||||
"Education": "Hezkuntza",
|
||||
"Gifts": "Opariak",
|
||||
"Insurance": "Asegurua",
|
||||
"Medical Expenses": "Osasun gastuak",
|
||||
"Taxes": "Zergak"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Garraioa",
|
||||
"Transportation": "Garraioa",
|
||||
"Bicycle": "Bizikleta",
|
||||
"Bus/Train": "Autobusa/Trena",
|
||||
"Car": "Autoa",
|
||||
"Gas/Fuel": "Gasolina/Erregaia",
|
||||
"Hotel": "Hotela",
|
||||
"Parking": "Aparkalekua",
|
||||
"Plane": "Hegazkina",
|
||||
"Taxi": "Taxia"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Erabilgarritasunak",
|
||||
"Utilities": "Erabilgarritasunak",
|
||||
"Cleaning": "Garbiketa",
|
||||
"Electricity": "Elektrizitatea",
|
||||
"Heat/Gas": "Berokuntza/Gasa",
|
||||
"Trash": "Zaborra",
|
||||
"TV/Phone/Internet": "TV/Telefonoa/Interneta",
|
||||
"Water": "Ura"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Bilatu moneta...",
|
||||
"noCurrency": "Ez da monetarik aurkitu.",
|
||||
"custom": {
|
||||
"heading": "Pertsonalizatua"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Ohikoena"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Beste monetak"
|
||||
}
|
||||
}
|
||||
}
|
||||
379
messages/fi.json
Normal file
@@ -0,0 +1,379 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Jaa kulut ystävien ja perheen kanssa",
|
||||
"description": "Tervetuloa uuteen Spliit-instanssiisi!",
|
||||
"button": {
|
||||
"groups": "Siirry ryhmiin",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Ryhmät"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Made in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Tekijät: <author>Sebastien Castiel</author> ja <source>muut osallistujat</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Kulut",
|
||||
"description": "Tässä ovat ryhmässä luodut kulut.",
|
||||
"create": "Lisää kulu",
|
||||
"createFirst": "Lisää ensimmäinen kulu",
|
||||
"noExpenses": "Ryhmälläsi ei ole vielä yhtään kulua.",
|
||||
"exportJson": "Vie JSON-tiedostoon",
|
||||
"exportCsv": "Vie CSV-tiedostoon",
|
||||
"searchPlaceholder": "Etsi kulua…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Kuka olet?",
|
||||
"description": "Valitse kuka osallistujista olet, jotta tiedot näkyvät oikein.",
|
||||
"nobody": "En halua valita ketään",
|
||||
"save": "Tallenna muutokset",
|
||||
"footer": "Tämän asetuksen voi vaihtaa myöhemmin ryhmän asetuksista."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Tulevat",
|
||||
"thisWeek": "Tällä viikolla",
|
||||
"earlierThisMonth": "Aikaisemmin tässä kuussa",
|
||||
"lastMonth": "Viime kuussa",
|
||||
"earlierThisYear": "Aikaisemmin tänä vuonna",
|
||||
"lastYear": "Viime vuonna",
|
||||
"older": "Vanhemmat"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong> maksoi {forCount, plural, =1 {henkilön} other {henkilöiden}} <paidFor></paidFor> puolesta",
|
||||
"receivedBy": "<strong>{paidBy}</strong> sai rahaa {forCount, plural, =1 {henkilön} other {henkilöiden}} <paidFor></paidFor> puolesta",
|
||||
"yourBalance": "Saldosi:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Omat ryhmät",
|
||||
"create": "Luo ryhmä",
|
||||
"loadingRecent": "Ladataan äskettäisiä ryhmiä…",
|
||||
"NoRecent": {
|
||||
"description": "Et ole ollut missään ryhmässä äskettäin.",
|
||||
"create": "Luo uusi ryhmä",
|
||||
"orAsk": "tai pyydä ystävää lähettämään linkki olemassaolevaan ryhmään."
|
||||
},
|
||||
"recent": "Äskettäiset",
|
||||
"starred": "Suosikit",
|
||||
"archived": "Arkistoidut",
|
||||
"archive": "Arkistoi ryhmä",
|
||||
"unarchive": "Palauta ryhmä arkistosta",
|
||||
"removeRecent": "Poista äskettäisistä",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Ryhmä poistettu",
|
||||
"description": "Ryhmä poistettu äskettäisten listaltasi.",
|
||||
"undoAlt": "Peruuta ryhmän poisto",
|
||||
"undo": "Peruuta"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Lisää URLilla",
|
||||
"title": "Lisää ryhmä URL-osoitteella",
|
||||
"description": "Jos ryhmä on jaettu sinulle, voit lisätä sen listaasi liittämällä URL-osoitteen tähän.",
|
||||
"error": "Hups, emme löytäneet ryhmää antamastasi URL-osoitteesta…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Tätä ryhmää ei löydy.",
|
||||
"link": "Siirry äskettäisiin ryhmiin"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Ryhmän tiedot",
|
||||
"NameField": {
|
||||
"label": "Ryhmän nimi",
|
||||
"placeholder": "Kesälomareissu",
|
||||
"description": "Syötä ryhmäsi nimi."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Ryhmän tiedot",
|
||||
"placeholder": "Mitkä tiedot ovat merkityksellisiä ryhmän osallistujille?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Valuuttamerkki",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Näytetään rahasummien yhteydessä."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Osallistujat",
|
||||
"description": "Syötä jokaisen osallistujan nimi.",
|
||||
"protectedParticipant": "Tätä osallistujaa ei voida poistaa, koska hän osallistuu kuluihin.",
|
||||
"add": "Lisää osallistuja",
|
||||
"new": "Uusi",
|
||||
"John": "Antti",
|
||||
"Jane": "Laura",
|
||||
"Jack": "Jussi"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Paikalliset asetukset",
|
||||
"description": "Nämä asetukset ovat laitekohtaisia. Voit muokata niillä käytettävyyttä.",
|
||||
"ActiveUserField": {
|
||||
"label": "Aktiivinen käyttäjä",
|
||||
"placeholder": "Valitse osallistuja",
|
||||
"none": "Ei kukaan",
|
||||
"description": "Käytetään kulujen oletusmaksajana."
|
||||
},
|
||||
"save": "Tallenna",
|
||||
"saving": "Tallennetaan…",
|
||||
"create": "Luo ryhmä",
|
||||
"creating": "Luodaan…",
|
||||
"cancel": "Peruuta"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Lisää tulo",
|
||||
"edit": "Muokkaa tuloa",
|
||||
"TitleField": {
|
||||
"label": "Otsikko",
|
||||
"placeholder": "Maanantain ravintola",
|
||||
"description": "Anna lyhyt kuvaus tulolle."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Päivä",
|
||||
"description": "Valitse päivä jolloin tulo saatiin."
|
||||
},
|
||||
"categoryFieldDescription": "Valitse tulokategoria.",
|
||||
"paidByField": {
|
||||
"label": "Vastaanottaja",
|
||||
"description": "Valitse kuka vastaanotti tulon."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Tulon jakaminen",
|
||||
"description": "Valitse kenelle tulo jaetaan."
|
||||
},
|
||||
"splitModeDescription": "Valitse miten tulo jaetaan osallistujien kesken.",
|
||||
"attachDescription": "Katso ja liitä tuloon liittyviä kuitteja."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Lisää kulu",
|
||||
"edit": "Muokkaa kulua",
|
||||
"TitleField": {
|
||||
"label": "Otsikko",
|
||||
"placeholder": "Maanantain ravintola",
|
||||
"description": "Anna lyhyt kuvaus kululle."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Päivä",
|
||||
"description": "Valitse päivä jolloin kulu maksettiin."
|
||||
},
|
||||
"categoryFieldDescription": "Valitse kulukategoria.",
|
||||
"paidByField": {
|
||||
"label": "Maksaja",
|
||||
"description": "Valitse kuka maksoi kulun."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Kulun jakaminen",
|
||||
"description": "Valitse ketkä osallistuvat kuluun."
|
||||
},
|
||||
"splitModeDescription": "Valitse miten kulu jaetaan osallistujien kesken.",
|
||||
"attachDescription": "Katso ja liitä kuluun liittyviä kuitteja."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Summa"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Tämä on velanmaksu"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Muistiinpanot"
|
||||
},
|
||||
"selectNone": "Tyhjennä valinnat",
|
||||
"selectAll": "Valitse kaikki",
|
||||
"shares": "osuutta",
|
||||
"advancedOptions": "Lisäasetuksia jakamiseen…",
|
||||
"SplitModeField": {
|
||||
"label": "Jakamistapa",
|
||||
"evenly": "Tasan",
|
||||
"byShares": "Epätasan – osuuksien mukaan",
|
||||
"byPercentage": "Epätasan – prosenttien mukaan",
|
||||
"byAmount": "Epätasan – summan mukaan",
|
||||
"saveAsDefault": "Tallenna oletustavaksi"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Poista",
|
||||
"title": "Poistetaanko tämä kulu?",
|
||||
"description": "Haluatko varmasti poistaa tämän kulun? Poistoa ei voi peruuttaa.",
|
||||
"yes": "Kyllä",
|
||||
"cancel": "Peruuta"
|
||||
},
|
||||
"attachDocuments": "Liitä dokumenttejä",
|
||||
"create": "Lisää kulu",
|
||||
"creating": "Luodaan kulua…",
|
||||
"save": "Tallenna",
|
||||
"saving": "Tallennetaan…",
|
||||
"cancel": "Peruuta",
|
||||
"reimbursement": "Velanmaksu"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Tiedosto on liian suuri",
|
||||
"description": "Maksimikoko ladattavalle tiedostolle on {maxSize}. Tiedostosi on {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Virhe tiedostoa ladattaessa",
|
||||
"description": "Jokin meni vikaan dokumentin lataamisessa. Yritä myöhemmin uudelleen tai valitse toinen tiedosto.",
|
||||
"retry": "Yritä uudelleen"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Luo kulu kuitista",
|
||||
"title": "Luo kuitista",
|
||||
"description": "Lue kuitin valokuvasta kulun tiedot.",
|
||||
"body": "Lataa kuitista valokuva. Siitä skannataan tiedot kulua varten.",
|
||||
"selectImage": "Valitse kuva…",
|
||||
"titleLabel": "Otsikko:",
|
||||
"categoryLabel": "Kategoria:",
|
||||
"amountLabel": "Summa:",
|
||||
"dateLabel": "Päivä:",
|
||||
"editNext": "Voit muokata kulun tietoja seuraavaksi.",
|
||||
"continue": "Jatka"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Saldo",
|
||||
"description": "Osallistujien saatavat tai velat.",
|
||||
"Reimbursements": {
|
||||
"title": "Maksuehdotus",
|
||||
"description": "Optimoitu ehdotus kuka maksaa kenellekin.",
|
||||
"noImbursements": "Näyttää siltä, että kaikki ovat sujut 😁",
|
||||
"owes": "<strong>{from}</strong> maksaa henkilölle <strong>{to}</strong>",
|
||||
"markAsPaid": "Merkitse maksetuksi"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Tilastot",
|
||||
"Totals": {
|
||||
"title": "Yhteenveto",
|
||||
"description": "Koko ryhmän kulut.",
|
||||
"groupSpendings": "Koko ryhmän kulutus",
|
||||
"groupEarnings": "Koko ryhmän saatavat",
|
||||
"yourSpendings": "Kulutuksesi",
|
||||
"yourEarnings": "Saatavasi",
|
||||
"yourShare": "Osuutesi"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Tapahtumat",
|
||||
"description": "Yleisnäkymä ryhmän kaikista tapahtumista.",
|
||||
"noActivity": "Ryhmässäsi ei ole vielä tapahtumia.",
|
||||
"someone": "Tuntematon",
|
||||
"settingsModified": "<strong>{participant}</strong> muokkasi ryhmän asetuksia.",
|
||||
"expenseCreated": "<strong>{participant}</strong> lisäsi kulun <em>{expense}</em>.",
|
||||
"expenseUpdated": "<strong>{participant}</strong> muokkasi kulua <em>{expense}</em>.",
|
||||
"expenseDeleted": "<strong>{participant}</strong> poisti kulun <em>{expense}</em>.",
|
||||
"Groups": {
|
||||
"today": "Tänään",
|
||||
"yesterday": "Eilen",
|
||||
"earlierThisWeek": "Tällä viikolla",
|
||||
"lastWeek": "Viime viikolla",
|
||||
"earlierThisMonth": "Tässä kuussa",
|
||||
"lastMonth": "Viime kuussa",
|
||||
"earlierThisYear": "Tänä vuonna",
|
||||
"lastYear": "Viime vuonna",
|
||||
"older": "Vanhemmat"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Tiedot",
|
||||
"description": "Käytä tätä paikkaa lisätäksesi kaikki tiedot, joilla voi olla merkitystä ryhmän osallistujille.",
|
||||
"empty": "Ryhmätietoja ei vielä ole."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Asetukset"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Jaa",
|
||||
"description": "Jaa ryhmän URL muille jäsenille, jotta he voivat nähdä sen ja lisätä kuluja.",
|
||||
"warning": "Varoitus!",
|
||||
"warningHelp": "Tällä URLilla kuka tahansa pääsee näkemään ja muokkaamaan kuluja. Jaa harkiten!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Syötä vähintään yksi merkki.",
|
||||
"min2": "Syötä vähintään kaksi merkkiä.",
|
||||
"max5": "Syötä enintään viisi merkkiä.",
|
||||
"max50": "Syötä enintään 50 merkkiä.",
|
||||
"duplicateParticipantName": "Tämä nimi on jo toisella osallistujalla.",
|
||||
"titleRequired": "Otsikko puuttuu.",
|
||||
"invalidNumber": "Epäkelpo numero.",
|
||||
"amountRequired": "Summa puuttuu.",
|
||||
"amountNotZero": "Summa ei voi olla nolla.",
|
||||
"amountTenMillion": "Summan pitää olla pienempi kuin 10 000 000.",
|
||||
"paidByRequired": "Osallistuja puuttuu.",
|
||||
"paidForMin1": "Valitse vähintään yksi osallistuja.",
|
||||
"noZeroShares": "Jokaisen osuuden täytyy olla suurempi kuin 0.",
|
||||
"amountSum": "Osuuksien summan täytyy vastata kulun summaa.",
|
||||
"percentageSum": "Prosenttiosuuksien summan täytyy olla 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Etsi kategoriaa...",
|
||||
"noCategory": "Kategoriaa ei löydy.",
|
||||
"Uncategorized": {
|
||||
"heading": "Yleiset",
|
||||
"General": "Yleinen",
|
||||
"Payment": "Maksu"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Viihde",
|
||||
"Entertainment": "Viihde",
|
||||
"Games": "Pelit",
|
||||
"Movies": "Elokuvat",
|
||||
"Music": "Musiikki",
|
||||
"Sports": "Urheilu"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Ruoka ja juoma",
|
||||
"Food and Drink": "Ruoka ja juoma",
|
||||
"Dining Out": "Ulkona syöminen",
|
||||
"Groceries": "Marketti",
|
||||
"Liquor": "Alkoholi"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Koti",
|
||||
"Home": "Koti",
|
||||
"Electronics": "Elektroniikka",
|
||||
"Furniture": "Huonekalut",
|
||||
"Household Supplies": "Taloustavarat",
|
||||
"Maintenance": "Huolto",
|
||||
"Mortgage": "Laina",
|
||||
"Pets": "Lemmikit",
|
||||
"Rent": "Vuokra",
|
||||
"Services": "Palvelut"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Elämä",
|
||||
"Childcare": "Lastenhoito",
|
||||
"Clothing": "Vaatteet",
|
||||
"Education": "Opiskelu",
|
||||
"Gifts": "Lahjat",
|
||||
"Insurance": "Vakuutukset",
|
||||
"Medical Expenses": "Terveydenhoito",
|
||||
"Taxes": "Verot"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Liikenne",
|
||||
"Transportation": "Liikenne",
|
||||
"Bicycle": "Polkupyörä",
|
||||
"Bus/Train": "Bussi/juna",
|
||||
"Car": "Auto",
|
||||
"Gas/Fuel": "Polttoaine",
|
||||
"Hotel": "Hotelli",
|
||||
"Parking": "Pysäköinti",
|
||||
"Plane": "Lentäminen",
|
||||
"Taxi": "Taksi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Sekalaiset",
|
||||
"Utilities": "Sekalaiset",
|
||||
"Cleaning": "Siivous",
|
||||
"Electricity": "Sähkö",
|
||||
"Heat/Gas": "Lämmitys",
|
||||
"Trash": "Jätehuolto",
|
||||
"TV/Phone/Internet": "TV/Puhelin/Internet",
|
||||
"Water": "Vesi"
|
||||
}
|
||||
}
|
||||
}
|
||||
459
messages/fr-FR.json
Normal file
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Partagez <strong>vos dépenses</strong> avec <strong>vos amis & votre famille</strong>",
|
||||
"description": "Bienvenue sur votre instance <strong>Spliit</strong> !",
|
||||
"button": {
|
||||
"groups": "Accéder aux groupes",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Groupes"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Made in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Développé par <author>Sebastien Castiel</author> et <source>contributeurs</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Dépenses",
|
||||
"description": "Voici les dépenses que vous avez créées pour votre groupe.",
|
||||
"create": "Créer une dépense",
|
||||
"createFirst": "Créer la première :)",
|
||||
"noExpenses": "Votre groupe n'a effectué aucune dépense pour le moment.",
|
||||
"exportJson": "Exporter en JSON",
|
||||
"exportCsv": "Exporter en CSV",
|
||||
"searchPlaceholder": "Rechercher une dépense…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Qui êtes-vous ?",
|
||||
"description": "Dites-nous quel participant vous êtes pour personnaliser l'affichage des informations.",
|
||||
"nobody": "Je ne veux sélectionner personne",
|
||||
"save": "Sauvegarder les modifications",
|
||||
"footer": "Ce paramètre peut être modifié plus tard dans les paramètres du groupe."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "À venir",
|
||||
"thisWeek": "Cette semaine",
|
||||
"earlierThisMonth": "Plus tôt ce mois-ci",
|
||||
"lastMonth": "Le mois dernier",
|
||||
"earlierThisYear": "Plus tôt cette année",
|
||||
"lastYear": "L'année dernière",
|
||||
"older": "Plus ancien"
|
||||
},
|
||||
"export": "Exporter"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Payé par <strong>{paidBy}</strong> pour <paidFor></paidFor>",
|
||||
"receivedBy": "Reçu par <strong>{paidBy}</strong> pour <paidFor></paidFor>",
|
||||
"yourBalance": "Votre solde :",
|
||||
"everyone": "tout le monde",
|
||||
"notInvolved": "Vous n'êtes pas concerné"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Mes groupes",
|
||||
"create": "Créer",
|
||||
"loadingRecent": "Chargement des groupes récents…",
|
||||
"NoRecent": {
|
||||
"description": "Vous n'avez visité aucun groupe récemment.",
|
||||
"create": "Créer un groupe",
|
||||
"orAsk": "ou demandez à un ami de vous envoyer le lien d'un groupe existant."
|
||||
},
|
||||
"recent": "Groupes récents",
|
||||
"starred": "Groupes favoris",
|
||||
"archived": "Groupes archivés",
|
||||
"archive": "Archiver le groupe",
|
||||
"unarchive": "Désarchiver le groupe",
|
||||
"removeRecent": "Supprimer des groupes récents",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Le groupe a été supprimé",
|
||||
"description": "Le groupe a été supprimé de votre liste de groupes récents.",
|
||||
"undoAlt": "Annuler la suppression du groupe",
|
||||
"undo": "Annuler"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Ajouter par URL",
|
||||
"title": "Ajouter un groupe par URL",
|
||||
"description": "Si un groupe a été partagé avec vous, vous pouvez coller son URL ici pour l'ajouter à votre liste.",
|
||||
"error": "Oups, nous ne pouvons pas trouver le groupe à partir de l'URL que vous avez fournie…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Ce groupe n'existe pas.",
|
||||
"link": "Aller aux groupes récemment visités"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informations sur le groupe",
|
||||
"NameField": {
|
||||
"label": "Nom du groupe",
|
||||
"placeholder": "Vacances d'été",
|
||||
"description": "Entrez un nom pour votre groupe."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informations sur le groupe",
|
||||
"placeholder": "Quelles informations sont pertinentes pour les participants du groupe ?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Symbole monétaire",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Nous l'utiliserons pour afficher les montants."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participants",
|
||||
"description": "Entrez le nom de chaque participant.",
|
||||
"protectedParticipant": "Ce participant fait partie des dépenses et ne peut pas être supprimé.",
|
||||
"new": "Nouveau",
|
||||
"add": "Ajouter un participant",
|
||||
"John": "Jean",
|
||||
"Jane": "Jeanne",
|
||||
"Jack": "Jacques"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Paramètres locaux",
|
||||
"description": "Ces paramètres sont définis par appareil et sont utilisés pour personnaliser votre expérience.",
|
||||
"ActiveUserField": {
|
||||
"label": "Utilisateur actif",
|
||||
"placeholder": "Sélectionner un participant",
|
||||
"none": "Aucun",
|
||||
"description": "Utilisateur utilisé comme défaut pour payer les dépenses."
|
||||
},
|
||||
"save": "Sauvegarder",
|
||||
"saving": "Sauvegarde…",
|
||||
"create": "Créer",
|
||||
"creating": "Création…",
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Devise principale",
|
||||
"createDescription": "Tous les montants et soldes seront dans cette devise.",
|
||||
"editDescription": "Tous les montants et soldes seront exprimés dans cette devise. La modification de cette option n'entraînera PAS la conversion des dépenses déjà saisies, sauf si la devise a des « unités mineures » différentes de celles de la devise actuelle (par exemple, passage du dollar américain au yen japonais)",
|
||||
"customOption": "Personnalisée"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Créer un revenu",
|
||||
"edit": "Modifier le revenu",
|
||||
"TitleField": {
|
||||
"label": "Titre du revenu",
|
||||
"placeholder": "Restaurant du lundi soir",
|
||||
"description": "Entrez une description pour le revenu."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Date du revenu",
|
||||
"description": "Entrez la date à laquelle le revenu a été reçu."
|
||||
},
|
||||
"categoryFieldDescription": "Sélectionnez la catégorie de revenu.",
|
||||
"paidByField": {
|
||||
"label": "Reçu par",
|
||||
"description": "Sélectionnez le participant qui a reçu le revenu."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Récurrence de la dépense",
|
||||
"description": "Sélectionnez la fréquence de répétition de la dépense.",
|
||||
"none": "Aucune",
|
||||
"daily": "Quotidienne",
|
||||
"weekly": "Hebdomadaire",
|
||||
"monthly": "Mensuelle"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Reçu pour",
|
||||
"description": "Sélectionnez pour qui le revenu a été reçu."
|
||||
},
|
||||
"splitModeDescription": "Sélectionnez comment diviser le revenu.",
|
||||
"attachDescription": "Voir et joindre des reçus au revenu.",
|
||||
"currencyField": {
|
||||
"label": "Devise de la recette",
|
||||
"description": "La devise dans laquelle le revenu a été reçu."
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Créer une dépense",
|
||||
"edit": "Modifier la dépense",
|
||||
"TitleField": {
|
||||
"label": "Titre de la dépense",
|
||||
"placeholder": "Restaurant du lundi soir",
|
||||
"description": "Entrez une description pour la dépense."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Date de la dépense",
|
||||
"description": "Entrez la date à laquelle la dépense a été payée."
|
||||
},
|
||||
"categoryFieldDescription": "Sélectionnez la catégorie de dépense.",
|
||||
"paidByField": {
|
||||
"label": "Payé par",
|
||||
"description": "Sélectionnez le participant qui a réglé la dépense.",
|
||||
"placeholder": "Sélectionner un participant"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Récurrence de la dépense",
|
||||
"description": "Sélectionnez la fréquence de répétition de la dépense.",
|
||||
"none": "Aucune",
|
||||
"daily": "Quotidienne",
|
||||
"weekly": "Hebdomadaire",
|
||||
"monthly": "Mensuelle"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Payé pour",
|
||||
"description": "Sélectionnez les participants concernés."
|
||||
},
|
||||
"splitModeDescription": "Sélectionnez comment diviser la dépense.",
|
||||
"attachDescription": "Voir et joindre des reçus à la dépense.",
|
||||
"currencyField": {
|
||||
"label": "Devise de la dépense",
|
||||
"description": "La devise dans laquelle la dépense a été payée."
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Montant"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "C'est un remboursement"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Catégorie"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notes"
|
||||
},
|
||||
"selectNone": "Tout désélectionner",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"shares": "part(s)",
|
||||
"advancedOptions": "Options de répartition avancées…",
|
||||
"SplitModeField": {
|
||||
"label": "Mode de répartition",
|
||||
"evenly": "Également",
|
||||
"byShares": "Inégalement – Par parts",
|
||||
"byPercentage": "Inégalement – Par pourcentage",
|
||||
"byAmount": "Inégalement – Par montant",
|
||||
"saveAsDefault": "Enregistrer comme options de répartition par défaut"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Supprimer",
|
||||
"title": "Supprimer cette dépense ?",
|
||||
"description": "Voulez-vous vraiment supprimer cette dépense ? Cette action est irréversible.",
|
||||
"yes": "Oui",
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
"attachDocuments": "Joindre des documents",
|
||||
"create": "Créer",
|
||||
"creating": "Création…",
|
||||
"save": "Sauvegarder",
|
||||
"saving": "Sauvegarde…",
|
||||
"cancel": "Annuler",
|
||||
"reimbursement": "Remboursement",
|
||||
"conversionUnavailable": "Pour définir une devise différente pour chaque dépense et convertir les montants, sélectionnez une devise non personnalisée pour le groupe.",
|
||||
"originalAmountField": {
|
||||
"label": "Montant à convertir"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useCustom": "Utiliser un taux personnalisé",
|
||||
"label": "Taux de change",
|
||||
"useApi": "Utiliser les taux de change de Frankfurter"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Obtention des taux de change…",
|
||||
"success": "Taux obtenus :",
|
||||
"error": "Oups, nous n'avons pas pu obtenir les taux de change les plus récents.",
|
||||
"refresh": "Actualiser",
|
||||
"staleRate": "Taux de change utilisé :",
|
||||
"noRate": "Saisissez un taux de change personnalisé ci-dessous.",
|
||||
"currencyNotFound": "Oups, Frankfurter n’a pas le taux de change pour cette devise à cette date.",
|
||||
"noDate": "Saisissez la date de la dépense pour obtenir un taux de change.",
|
||||
"dateMismatch": "Taux de change le {date}",
|
||||
"customRate": "Utilisation d’un taux personnalisé"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Le fichier est trop grand",
|
||||
"description": "La taille maximale du fichier que vous pouvez télécharger est {maxSize}. La vôtre est {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Erreur lors du téléchargement du document",
|
||||
"description": "Un problème est survenu lors du téléchargement du document. Veuillez réessayer plus tard ou sélectionner un fichier différent.",
|
||||
"retry": "Réessayer"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Créer une dépense à partir du reçu",
|
||||
"title": "Créer à partir du reçu",
|
||||
"description": "Extraire les informations de la dépense à partir d'une photo de reçu.",
|
||||
"body": "Téléchargez la photo d'un reçu, et nous l'analyserons pour extraire les informations de la dépense si possible.",
|
||||
"selectImage": "Sélectionner une image…",
|
||||
"titleLabel": "Titre :",
|
||||
"categoryLabel": "Catégorie :",
|
||||
"amountLabel": "Montant :",
|
||||
"dateLabel": "Date :",
|
||||
"editNext": "Vous pourrez modifier les informations de la dépense ensuite.",
|
||||
"continue": "Continuer"
|
||||
},
|
||||
"unknown": "Inconnu",
|
||||
"TooBigToast": {
|
||||
"title": "Le fichier est trop grand",
|
||||
"description": "La taille maximale du fichier que vous pouvez télécharger est {maxSize}. La vôtre est {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Erreur lors du téléchargement du document",
|
||||
"description": "Un problème est survenu lors du téléchargement du document. Veuillez réessayer plus tard ou sélectionner un fichier différent.",
|
||||
"retry": "Réessayer"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Équilibres",
|
||||
"description": "Voici le montant que chaque participant a payé ou doit rembourser.",
|
||||
"Reimbursements": {
|
||||
"title": "Remboursements suggérés",
|
||||
"description": "Voici des suggestions pour des remboursements optimisés entre les participants.",
|
||||
"noImbursements": "Les dépenses effectuées ne nécessitent pas d'équilibrage 😁",
|
||||
"owes": "<strong>{from}</strong> doit à <strong>{to}</strong>",
|
||||
"markAsPaid": "Marquer comme payé"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistiques",
|
||||
"Totals": {
|
||||
"title": "Totaux",
|
||||
"description": "Résumé des dépenses du groupe entier.",
|
||||
"groupSpendings": "Total des dépenses du groupe",
|
||||
"groupEarnings": "Total des revenus du groupe",
|
||||
"yourSpendings": "Vos dépenses totales",
|
||||
"yourEarnings": "Vos revenus totaux",
|
||||
"yourShare": "Votre part totale"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Activité",
|
||||
"description": "Vue d'ensemble de toute l'activité dans ce groupe.",
|
||||
"noActivity": "Il n'y a pas encore d'activité dans votre groupe.",
|
||||
"someone": "Quelqu'un",
|
||||
"settingsModified": "Les paramètres du groupe ont été modifiés par <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Dépense <em>{expense}</em> créée par <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Dépense <em>{expense}</em> mise à jour par <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Dépense <em>{expense}</em> supprimée par <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Aujourd'hui",
|
||||
"yesterday": "Hier",
|
||||
"earlierThisWeek": "Plus tôt cette semaine",
|
||||
"lastWeek": "La semaine dernière",
|
||||
"earlierThisMonth": "Plus tôt ce mois-ci",
|
||||
"lastMonth": "Le mois dernier",
|
||||
"earlierThisYear": "Plus tôt cette année",
|
||||
"lastYear": "L'année dernière",
|
||||
"older": "Plus ancien"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Information",
|
||||
"description": "Utilisez cet espace pour ajouter toute information qui pourrait être pertinente pour les participants du groupe.",
|
||||
"empty": "Aucune information pour le moment."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Paramètres"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Partager",
|
||||
"description": "Pour que d'autres participants puissent voir le groupe et ajouter des dépenses, partagez son URL avec eux.",
|
||||
"warning": "Avertissement !",
|
||||
"warningHelp": "Toute personne ayant l'URL du groupe pourra voir et modifier les dépenses. Partagez avec prudence !"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Entrez au moins un caractère.",
|
||||
"min2": "Entrez au moins deux caractères.",
|
||||
"max5": "Entrez au maximum cinq caractères.",
|
||||
"max50": "Entrez au maximum 50 caractères.",
|
||||
"duplicateParticipantName": "Un autre participant a déjà ce nom.",
|
||||
"titleRequired": "Veuillez entrer un titre.",
|
||||
"invalidNumber": "Nombre invalide.",
|
||||
"amountRequired": "Vous devez entrer un montant.",
|
||||
"amountNotZero": "Le montant ne doit pas être zéro.",
|
||||
"amountTenMillion": "Le montant doit être inférieur à 10 000 000.",
|
||||
"paidByRequired": "Vous devez sélectionner un participant.",
|
||||
"paidForMin1": "La dépense doit concerner au moins un participant.",
|
||||
"noZeroShares": "Toutes les parts doivent être supérieures à 0.",
|
||||
"amountSum": "La somme des montants doit être égale au montant de la dépense.",
|
||||
"percentageSum": "La somme des pourcentages doit être égale à 100.",
|
||||
"ratePositive": "Le taux de change doit être strictement supérieur à zéro."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Rechercher une catégorie…",
|
||||
"noCategory": "Aucune catégorie trouvée.",
|
||||
"Uncategorized": {
|
||||
"heading": "Non classé",
|
||||
"General": "Général",
|
||||
"Payment": "Paiement"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Divertissement",
|
||||
"Entertainment": "Divertissement",
|
||||
"Games": "Jeux",
|
||||
"Movies": "Films",
|
||||
"Music": "Musique",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Nourriture et boissons",
|
||||
"Food and Drink": "Nourriture et boissons",
|
||||
"Dining Out": "Repas au restaurant",
|
||||
"Groceries": "Épicerie",
|
||||
"Liquor": "Alcool"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Maison",
|
||||
"Home": "Maison",
|
||||
"Electronics": "Électronique",
|
||||
"Furniture": "Mobilier",
|
||||
"Household Supplies": "Fournitures ménagères",
|
||||
"Maintenance": "Entretien",
|
||||
"Mortgage": "Hypothèque",
|
||||
"Pets": "Animaux",
|
||||
"Rent": "Loyer",
|
||||
"Services": "Services"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Vie",
|
||||
"Childcare": "Garde d'enfants",
|
||||
"Clothing": "Vêtements",
|
||||
"Education": "Éducation",
|
||||
"Gifts": "Cadeaux",
|
||||
"Insurance": "Assurance",
|
||||
"Medical Expenses": "Dépenses médicales",
|
||||
"Taxes": "Impôts",
|
||||
"Donation": "Don"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transport",
|
||||
"Transportation": "Transport",
|
||||
"Bicycle": "Bicyclette",
|
||||
"Bus/Train": "Bus/Train",
|
||||
"Car": "Voiture",
|
||||
"Gas/Fuel": "Essence/Carburant",
|
||||
"Hotel": "Hôtel",
|
||||
"Parking": "Parking",
|
||||
"Plane": "Avion",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Services publics",
|
||||
"Utilities": "Services publics",
|
||||
"Cleaning": "Nettoyage",
|
||||
"Electricity": "Électricité",
|
||||
"Heat/Gas": "Chauffage/Gaz",
|
||||
"Trash": "Poubelle",
|
||||
"TV/Phone/Internet": "TV/Téléphone/Internet",
|
||||
"Water": "Eau"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Chercher une devise...",
|
||||
"noCurrency": "Aucune devise trouvée.",
|
||||
"custom": {
|
||||
"heading": "Personnalisée"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Les plus courantes"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Autres devises"
|
||||
}
|
||||
}
|
||||
}
|
||||
451
messages/he.json
Normal file
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "שתף <strong>הוצאות</strong> עם <strong>חברים ומשפחה</strong>",
|
||||
"description": "ברוך הבא למופע החדש שלך של <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "עבור לקבוצות",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "קבוצות"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "נבנה במונטריאול, קוויבק 🇨🇦",
|
||||
"builtBy": "נבנה על ידי <author>סבסטיאן קסטיאל</author> ו<source>תורמים</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "הוצאות",
|
||||
"description": "הנה ההוצאות שיצרת עבור הקבוצה שלך.",
|
||||
"createFirst": "צור את הראשונה",
|
||||
"create": "צור הוצאה",
|
||||
"noExpenses": "הקבוצה שלך עדיין לא מכילה הוצאות.",
|
||||
"export": "ייצא",
|
||||
"exportJson": "ייצא ל-JSON",
|
||||
"exportCsv": "ייצא ל-CSV",
|
||||
"searchPlaceholder": "חפש הוצאה…",
|
||||
"ActiveUserModal": {
|
||||
"title": "מי אתה?",
|
||||
"description": "ספר לנו איזה משתתף אתה כדי לאפשר לנו להתאים אישית את אופן הצגת המידע.",
|
||||
"nobody": "אני לא רוצה לבחור אף אחד",
|
||||
"save": "שמור שינויים",
|
||||
"footer": "הגדרה זו ניתנת לשינוי מאוחר יותר בהגדרות הקבוצה."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "מתקרבות",
|
||||
"thisWeek": "השבוע",
|
||||
"earlierThisMonth": "מוקדם יותר החודש",
|
||||
"lastMonth": "חודש שעבר",
|
||||
"earlierThisYear": "מוקדם יותר השנה",
|
||||
"lastYear": "שנה שעברה",
|
||||
"older": "ישנות יותר"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "שולם על ידי <strong>{paidBy}</strong> עבור <paidFor></paidFor>",
|
||||
"everyone": "כולם",
|
||||
"receivedBy": "התקבל על ידי <strong>{paidBy}</strong> עבור <paidFor></paidFor>",
|
||||
"yourBalance": "היתרה שלך:",
|
||||
"notInvolved": "אתה לא מעורב"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "הקבוצות שלי",
|
||||
"create": "צור",
|
||||
"loadingRecent": "טוען קבוצות אחרונות…",
|
||||
"NoRecent": {
|
||||
"description": "לא ביקרת באף קבוצה לאחרונה.",
|
||||
"create": "צור אחת",
|
||||
"orAsk": "או בקש מחבר לשלוח לך את הקישור לקבוצה קיימת."
|
||||
},
|
||||
"recent": "קבוצות אחרונות",
|
||||
"starred": "קבוצות מסומנות בכוכב",
|
||||
"archived": "קבוצות בארכיון",
|
||||
"archive": "העברת קבוצה לארכיון",
|
||||
"unarchive": "הוצאת קבוצה מארכיון",
|
||||
"removeRecent": "הסר מקבוצות אחרונות",
|
||||
"RecentRemovedToast": {
|
||||
"title": "הקבוצה הוסרה",
|
||||
"description": "הקבוצה הוסרה מרשימת הקבוצות האחרונות שלך.",
|
||||
"undoAlt": "בטל הסרת קבוצה",
|
||||
"undo": "בטל"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "הוסף באמצעות קישור",
|
||||
"title": "הוסף קבוצה באמצעות קישור",
|
||||
"description": "אם שיתפו איתך קבוצה, תוכל להדביק את הקישור שלה כאן כדי להוסיף אותה לרשימה שלך.",
|
||||
"error": "אופס, אנחנו לא מצליחים למצוא את הקבוצה מכתובת ה-URL שסיפקת…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "קבוצה זו אינה קיימת.",
|
||||
"link": "עבור לקבוצות שביקרת בהן לאחרונה"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "מידע על הקבוצה",
|
||||
"NameField": {
|
||||
"label": "שם הקבוצה",
|
||||
"placeholder": "חופשות קיץ",
|
||||
"description": "הזן שם לקבוצה שלך."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "מידע על הקבוצה",
|
||||
"placeholder": "איזה מידע רלוונטי למשתתפי הקבוצה?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "סמל מטבע",
|
||||
"placeholder": "$, €, ₪…",
|
||||
"description": "נשתמש בו כדי להציג סכומים."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "מטבע ראשי",
|
||||
"createDescription": "כל הסכומים והיתרות יהיו במטבע זה.",
|
||||
"editDescription": "כל הסכומים והיתרות יהיו במטבע זה. שינוי של זה לא ימיר הוצאות שכבר הוזנו, אלא אם כן למטבע יש \"יחידות משנה\" שונות מהנוכחיות (למשל, שינוי מדולר אמריקאי לין יפני)",
|
||||
"customOption": "מותאם אישית"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "משתתפים",
|
||||
"description": "הזן את השם עבור כל משתתף.",
|
||||
"protectedParticipant": "משתתף זה לקח חלק בהוצאות, ולא ניתן להסירו.",
|
||||
"new": "חדש",
|
||||
"add": "הוסף משתתף",
|
||||
"John": "אבי",
|
||||
"Jane": "ריקי",
|
||||
"Jack": "ג'קי"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "הגדרות מקומיות",
|
||||
"description": "הגדרות אלה מוגדרות לכל מכשיר בנפרד, ומשמשות להתאמה אישית של החוויה שלך.",
|
||||
"ActiveUserField": {
|
||||
"label": "משתמש פעיל",
|
||||
"placeholder": "בחר משתתף",
|
||||
"none": "אף אחד",
|
||||
"description": "משתמש המשמש כברירת מחדל לתשלום הוצאות."
|
||||
},
|
||||
"save": "שמור",
|
||||
"saving": "שומר…",
|
||||
"create": "צור",
|
||||
"creating": "יוצר…",
|
||||
"cancel": "ביטול"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "צור הכנסה",
|
||||
"edit": "ערוך הכנסה",
|
||||
"TitleField": {
|
||||
"label": "כותרת הכנסה",
|
||||
"placeholder": "מסעדה בערב יום שני",
|
||||
"description": "הזן תיאור עבור ההכנסה."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "תאריך ההכנסה",
|
||||
"description": "הזן את התאריך שבו התקבלה ההכנסה."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "מטבע ההכנסה",
|
||||
"description": "המטבע שבו התקבלה ההכנסה."
|
||||
},
|
||||
"categoryFieldDescription": "בחר את קטגוריית ההכנסה.",
|
||||
"paidByField": {
|
||||
"label": "ניתן על ידי",
|
||||
"description": "בחר את המשתתף שהעביר את ההכנסה."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "התקבל עבור",
|
||||
"description": "בחר עבור מי התקבלה ההכנסה."
|
||||
},
|
||||
"splitModeDescription": "בחר כיצד לפצל את ההכנסה.",
|
||||
"attachDescription": "ראה וצרף קבלות להכנסה."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "צור הוצאה",
|
||||
"edit": "ערוך הוצאה",
|
||||
"TitleField": {
|
||||
"label": "כותרת הוצאה",
|
||||
"placeholder": "מסעדה בערב יום שני",
|
||||
"description": "הזן תיאור עבור ההוצאה."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "תאריך הוצאה",
|
||||
"description": "הזן את התאריך בו שולמה ההוצאה."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "מטבע ההוצאה",
|
||||
"description": "המטבע שבו שולמה ההוצאה."
|
||||
},
|
||||
"categoryFieldDescription": "בחר את קטגוריית ההוצאה.",
|
||||
"paidByField": {
|
||||
"label": "שולם על ידי",
|
||||
"placeholder": "בחר משתתף",
|
||||
"description": "בחר את המשתתף ששילם את ההוצאה."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "חזרתיות הוצאה",
|
||||
"description": "בחר כמה פעמים ההוצאה צריכה לחזור.",
|
||||
"none": "ללא",
|
||||
"daily": "יומית",
|
||||
"weekly": "שבועית",
|
||||
"monthly": "חודשית"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "שולם עבור",
|
||||
"description": "בחר עבור מי שולמה ההוצאה."
|
||||
},
|
||||
"splitModeDescription": "בחר כיצד לפצל את ההוצאה.",
|
||||
"attachDescription": "ראה וצרף קבלות להוצאה."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "סכום"
|
||||
},
|
||||
"conversionUnavailable": "כדי להגדיר מטבע שונה לכל הוצאה ולהמיר סכומים, בחר מטבע לא מותאם אישית לקבוצה.",
|
||||
"originalAmountField": {
|
||||
"label": "סכום להמרה"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "השתמש בשערים מ-Frankfurter",
|
||||
"useCustom": "השתמש בשער מותאם אישית",
|
||||
"label": "שער חליפין"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "משיג שערי חליפין…",
|
||||
"refresh": "רענן",
|
||||
"customRate": "משתמש בשער מותאם אישית",
|
||||
"success": "הושגו שערים:",
|
||||
"error": "אופס, לא הצלחנו לקבל את השערים העדכניים ביותר.",
|
||||
"staleRate": "משתמש בשער:",
|
||||
"noRate": "הזן שער מותאם אישית למטה.",
|
||||
"currencyNotFound": "אופס, ל-Frankfurter אין את השער עבור מטבע זה ביום הזה.",
|
||||
"noDate": "הזן את תאריך ההוצאה כדי לקבל את שער החליפין.",
|
||||
"dateMismatch": "שערים מתאריך: {date}"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "זהו החזר"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "קטגוריה"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "הערות"
|
||||
},
|
||||
"selectNone": "הסר בחירה",
|
||||
"selectAll": "בחר הכל",
|
||||
"shares": "חלק(י) השתתפות",
|
||||
"advancedOptions": "אפשרויות פיצול מתקדמות…",
|
||||
"SplitModeField": {
|
||||
"label": "מצב פיצול",
|
||||
"evenly": "באופן שווה",
|
||||
"byShares": "לא שווה – לפי חלקי השתתפות",
|
||||
"byPercentage": "לא שווה – לפי אחוזים",
|
||||
"byAmount": "לא שווה – לפי סכום",
|
||||
"saveAsDefault": "שמור כברירת מחדל עבור אפשרויות פיצול"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "מחק",
|
||||
"title": "למחוק הוצאה זו?",
|
||||
"description": "האם אתה באמת רוצה למחוק הוצאה זו? פעולה זו בלתי הפיכה.",
|
||||
"yes": "כן",
|
||||
"cancel": "ביטול"
|
||||
},
|
||||
"attachDocuments": "צרף מסמכים",
|
||||
"create": "צור",
|
||||
"creating": "יוצר…",
|
||||
"save": "שמור",
|
||||
"saving": "שומר…",
|
||||
"cancel": "ביטול",
|
||||
"reimbursement": "החזר"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "הקובץ גדול מדי",
|
||||
"description": "גודל הקובץ המקסימלי שאתה יכול להעלות הוא {maxSize}. הקובץ שלך הוא {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "שגיאה בעת העלאת מסמך",
|
||||
"description": "משהו השתבש בעת העלאת המסמך. נסה שוב מאוחר יותר או בחר קובץ אחר.",
|
||||
"retry": "נסה שוב"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "צור הוצאה מקבלה",
|
||||
"title": "צור מקבלה",
|
||||
"description": "חלץ את מידע ההוצאה מתמונת קבלה.",
|
||||
"body": "העלה תמונה של קבלה, ואנחנו נסרוק אותה כדי לחלץ את מידע ההוצאה אם נוכל.",
|
||||
"selectImage": "בחר תמונה…",
|
||||
"titleLabel": "כותרת:",
|
||||
"categoryLabel": "קטגוריה:",
|
||||
"amountLabel": "סכום:",
|
||||
"dateLabel": "תאריך:",
|
||||
"editNext": "תוכל לערוך את מידע ההוצאה מאוחר יותר.",
|
||||
"continue": "המשך"
|
||||
},
|
||||
"unknown": "לא ידוע",
|
||||
"TooBigToast": {
|
||||
"title": "הקובץ גדול מדי",
|
||||
"description": "גודל הקובץ המקסימלי שאתה יכול להעלות הוא {maxSize}. הקובץ שלך הוא {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "שגיאה בעת העלאת מסמך",
|
||||
"description": "משהו השתבש בעת העלאת המסמך. נסה שוב מאוחר יותר או בחר קובץ אחר.",
|
||||
"retry": "נסה שוב"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "יתרות",
|
||||
"description": "זה הסכום שכל משתתף שילם או קיבל.",
|
||||
"Reimbursements": {
|
||||
"title": "החזרים מוצעים",
|
||||
"description": "הנה הצעות להחזרים אופטימליים בין משתתפים.",
|
||||
"noImbursements": "נראה שהקבוצה שלך לא צריכה החזרים 😁",
|
||||
"owes": "<strong>{from}</strong> חייב ל<strong>{to}</strong>",
|
||||
"markAsPaid": "סמן כשולם"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "סטטיסטיקות",
|
||||
"Totals": {
|
||||
"title": "סיכומים",
|
||||
"description": "סיכום הוצאות של כל הקבוצה.",
|
||||
"groupSpendings": "סך הוצאות הקבוצה",
|
||||
"groupEarnings": "סך הכנסות הקבוצה",
|
||||
"yourSpendings": "סך ההוצאות שלך",
|
||||
"yourEarnings": "סך ההכנסות שלך",
|
||||
"yourShare": "סך החלק שלך"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "פעילות",
|
||||
"description": "סקירה של כל הפעילות בקבוצה זו.",
|
||||
"noActivity": "עדיין אין פעילות בקבוצה שלך.",
|
||||
"someone": "מישהו",
|
||||
"settingsModified": "הגדרות הקבוצה שונו על ידי <strong>{participant}</strong>.",
|
||||
"expenseCreated": "הוצאה <em>{expense}</em> נוצרה על ידי <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "הוצאה <em>{expense}</em> עודכנה על ידי <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "הוצאה <em>{expense}</em> נמחקה על ידי <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "היום",
|
||||
"yesterday": "אתמול",
|
||||
"earlierThisWeek": "מוקדם יותר השבוע",
|
||||
"lastWeek": "שבוע שעבר",
|
||||
"earlierThisMonth": "מוקדם יותר החודש",
|
||||
"lastMonth": "חודש שעבר",
|
||||
"earlierThisYear": "מוקדם יותר השנה",
|
||||
"lastYear": "שנה שעברה",
|
||||
"older": "ישנות יותר"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "מידע",
|
||||
"description": "השתמש במקום זה כדי להוסיף כל מידע שיכול להיות רלוונטי למשתתפי הקבוצה.",
|
||||
"empty": "עדיין אין מידע על הקבוצה."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "הגדרות"
|
||||
},
|
||||
"Share": {
|
||||
"title": "שתף",
|
||||
"description": "כדי שמשתתפים אחרים יראו את הקבוצה ויוסיפו הוצאות, שתף איתם את הקישור שלה.",
|
||||
"warning": "אזהרה!",
|
||||
"warningHelp": "כל אדם עם קישור לקבוצה יוכל לראות ולערוך הוצאות. שתף בזהירות!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "הזן לפחות תו אחד.",
|
||||
"min2": "הזן לפחות שני תווים.",
|
||||
"max5": "הזן חמישה תווים לכל היותר.",
|
||||
"max50": "הזן 50 תווים לכל היותר.",
|
||||
"duplicateParticipantName": "קיים משתתף אחר בעל שם זהה.",
|
||||
"titleRequired": "נא להזין כותרת.",
|
||||
"invalidNumber": "מספר לא תקין.",
|
||||
"amountRequired": "עליך להזין סכום.",
|
||||
"amountNotZero": "הסכום לא יכול להיות אפס.",
|
||||
"amountTenMillion": "הסכום חייב להיות נמוך מ-10,000,000.",
|
||||
"ratePositive": "השער חייב להיות גדול מאפס באופן קפדני.",
|
||||
"paidByRequired": "עליך לבחור משתתף.",
|
||||
"paidForMin1": "ההוצאה חייבת להיות משולמת עבור לפחות משתתף אחד.",
|
||||
"noZeroShares": "כל חלקי ההשתתפות חייבים להיות גבוהים מ-0.",
|
||||
"amountSum": "סיכום הסכומים חייב להיות שווה לסכום ההוצאה.",
|
||||
"percentageSum": "סיכום האחוזים חייב להיות שווה ל-100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "חפש קטגוריה...",
|
||||
"noCategory": "לא נמצאה קטגוריה.",
|
||||
"Uncategorized": {
|
||||
"heading": "לא מסווג",
|
||||
"General": "כללי",
|
||||
"Payment": "תשלום"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "בידור",
|
||||
"Entertainment": "בידור",
|
||||
"Games": "משחקים",
|
||||
"Movies": "סרטים",
|
||||
"Music": "מוזיקה",
|
||||
"Sports": "ספורט"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "אוכל ומשקאות",
|
||||
"Food and Drink": "אוכל ומשקאות",
|
||||
"Dining Out": "אכילה בחוץ",
|
||||
"Groceries": "מצרכים",
|
||||
"Liquor": "משקאות חריפים"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "בית",
|
||||
"Home": "בית",
|
||||
"Electronics": "אלקטרוניקה",
|
||||
"Furniture": "ריהוט",
|
||||
"Household Supplies": "ציוד ביתי",
|
||||
"Maintenance": "תחזוקה",
|
||||
"Mortgage": "משכנתא",
|
||||
"Pets": "חיות מחמד",
|
||||
"Rent": "שכירות",
|
||||
"Services": "שירות(ים)"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "חיים",
|
||||
"Childcare": "טיפול בילדים",
|
||||
"Clothing": "ביגוד",
|
||||
"Donation": "תרומה",
|
||||
"Education": "חינוך",
|
||||
"Gifts": "מתנות",
|
||||
"Insurance": "ביטוח",
|
||||
"Medical Expenses": "הוצאות רפואיות",
|
||||
"Taxes": "מסים"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "תחבורה",
|
||||
"Transportation": "תחבורה",
|
||||
"Bicycle": "אופניים",
|
||||
"Bus/Train": "אוטובוס/רכבת",
|
||||
"Car": "רכב",
|
||||
"Gas/Fuel": "דלק/בנזין",
|
||||
"Hotel": "מלון",
|
||||
"Parking": "חניה",
|
||||
"Plane": "מטוס",
|
||||
"Taxi": "מונית"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "שירותים",
|
||||
"Utilities": "שירותים",
|
||||
"Cleaning": "ניקיון",
|
||||
"Electricity": "חשמל",
|
||||
"Heat/Gas": "חימום/גז",
|
||||
"Trash": "פסולת",
|
||||
"TV/Phone/Internet": "תקשורת",
|
||||
"Water": "מים"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "חפש מטבע...",
|
||||
"noCurrency": "לא נמצאו מטבעות.",
|
||||
"custom": {
|
||||
"heading": "מותאם אישית"
|
||||
},
|
||||
"common": {
|
||||
"heading": "הנפוצים ביותר"
|
||||
},
|
||||
"other": {
|
||||
"heading": "מטבעות אחרים"
|
||||
}
|
||||
}
|
||||
}
|
||||
451
messages/id.json
Normal file
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Bagikan <strong>Expenses</strong> dengan <strong>Teman & Keluarga</strong>",
|
||||
"description": "Selamat datang di <strong>Spliit</strong> instance yang baru !",
|
||||
"button": {
|
||||
"groups": "Ke grup",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grup"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Dibuat di Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Dibuat oleh <author>Sebastien Castiel</author> dan <source>kontributor lainnya</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Pengeluaran",
|
||||
"description": "Berikut adalah pengeluaran yang Anda buat untuk grup Anda.",
|
||||
"create": "Buat pengeluaran",
|
||||
"createFirst": "Buat yang pertama",
|
||||
"noExpenses": "Grup Anda belum memiliki pengeluaran apa pun.",
|
||||
"export": "Ekspor",
|
||||
"exportJson": "Ekspor ke JSON",
|
||||
"exportCsv": "Ekspor ke CSV",
|
||||
"searchPlaceholder": "Cari pengeluaran…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Siapa nama anda?",
|
||||
"description": "Beritahu kami nama Anda agar kami dapat menyesuaikan cara informasi ditampilkan.",
|
||||
"nobody": "Saya tidak ingin memilih siapa pun",
|
||||
"save": "Simpan perubahan",
|
||||
"footer": "Pengaturan ini dapat diubah nanti di pengaturan grup."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Mendatang",
|
||||
"thisWeek": "Minggu ini",
|
||||
"earlierThisMonth": "Awal bulan ini",
|
||||
"lastMonth": "Bulan lalu",
|
||||
"earlierThisYear": "Awal tahun ini",
|
||||
"lastYear": "Tahun lalu",
|
||||
"older": "Lebih tua"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Dibayar oleh <strong>{paidBy}</strong> untuk <paidFor></paidFor>",
|
||||
"everyone": "semua orang",
|
||||
"receivedBy": "Diterima oleh <strong>{paidBy}</strong> untuk <paidFor></paidFor>",
|
||||
"yourBalance": "Saldo Anda:",
|
||||
"notInvolved": "Anda tidak terlibat"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Grup saya",
|
||||
"create": "Buat",
|
||||
"loadingRecent": "Memuat grup terbaru…",
|
||||
"NoRecent": {
|
||||
"description": "Anda belum mengunjungi grup mana pun baru-baru ini.",
|
||||
"create": "Buat",
|
||||
"orAsk": "atau minta teman untuk mengirimi Anda tautan ke situs yang sudah ada."
|
||||
},
|
||||
"recent": "Grup terbaru",
|
||||
"starred": "Grup berbintang",
|
||||
"archived": "Grup terarsipkan",
|
||||
"archive": "Arsipkan grup",
|
||||
"unarchive": "Batalkan pengarsipan grup",
|
||||
"removeRecent": "Hapus dari grup terbaru",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Grup telah dihapus",
|
||||
"description": "Grup ini telah dihapus dari daftar grup terbaru Anda.",
|
||||
"undoAlt": "Urungkan penghapusan grup",
|
||||
"undo": "Urungkan"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Tambahkan melalui URL",
|
||||
"title": "Tambahkan grup melalui URL",
|
||||
"description": "Jika suatu grup dibagikan kepada Anda, Anda dapat menempelkan URL-nya di sini untuk menambahkannya ke daftar Anda.",
|
||||
"error": "Ups, kami tidak dapat menemukan grup dari URL yang Anda berikan…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Grup ini tidak dapat ditemukan.",
|
||||
"link": "Buka grup yang baru saja dikunjungi"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informasi grup",
|
||||
"NameField": {
|
||||
"label": "Nama grup",
|
||||
"placeholder": "Liburan sekolah",
|
||||
"description": "Masukkan nama untuk grup Anda."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informasi grup",
|
||||
"placeholder": "Informasi apa yang relevan bagi peserta kelompok?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Simbol mata uang",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Kita akan menggunakannya untuk menampilkan jumlah."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Mata uang utama",
|
||||
"createDescription": "Semua jumlah dan saldo akan dalam mata uang ini.",
|
||||
"editDescription": "Semua jumlah dan saldo akan menggunakan mata uang ini. Mengubah mata uang ini TIDAK akan mengonversi pengeluaran yang sudah dimasukkan, kecuali jika mata uang tersebut memiliki \"unit minor\" yang berbeda dari mata uang saat ini (misalnya, berubah dari Dolar AS ke Yen Jepang)",
|
||||
"customOption": "Kustom"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Peserta",
|
||||
"description": "Masukkan nama untuk setiap peserta.",
|
||||
"protectedParticipant": "Peserta ini adalah bagian dari pengeluaran, dan tidak dapat dihapus.",
|
||||
"new": "Baru",
|
||||
"add": "Tambahkan peserta",
|
||||
"John": "John",
|
||||
"Jane": "Janet",
|
||||
"Jack": "Jacksen"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Pengaturan lokal",
|
||||
"description": "Pengaturan ini ditetapkan per perangkat, dan digunakan untuk menyesuaikan pengalaman Anda.",
|
||||
"ActiveUserField": {
|
||||
"label": "Pengguna aktif",
|
||||
"placeholder": "Pilih peserta",
|
||||
"none": "Tidak ada",
|
||||
"description": "Pengguna digunakan sebagai default untuk membayar pengeluaran."
|
||||
},
|
||||
"save": "Simpan",
|
||||
"saving": "Meyimpan…",
|
||||
"create": "Buat",
|
||||
"creating": "Membuat…",
|
||||
"cancel": "Batalkan"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Buat pemasukan",
|
||||
"edit": "Ubah pemasukan",
|
||||
"TitleField": {
|
||||
"label": "Judul pemasukan",
|
||||
"placeholder": "Restoran Senin malam",
|
||||
"description": "Masukkan deskripsi untuk pemasukan."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Tanggal pemasukan",
|
||||
"description": "Masukkan tanggal penerimaan pemasukan."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Mata uang pemasukan",
|
||||
"description": "Mata uang di mana pemasukan diterima."
|
||||
},
|
||||
"categoryFieldDescription": "Pilih kategori pemasukan.",
|
||||
"paidByField": {
|
||||
"label": "Diterima oleh",
|
||||
"description": "Pilih peserta yang menerima pemasukan."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Diterima untuk",
|
||||
"description": "Pilih untuk siapa pemasukan tersebut diterima."
|
||||
},
|
||||
"splitModeDescription": "Pilih cara membagi pemasukan.",
|
||||
"attachDescription": "Lihat dan lampirkan tanda terima pada pemasukan."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Buat pengeluaran",
|
||||
"edit": "Ubah pengeluaran",
|
||||
"TitleField": {
|
||||
"label": "Judul pengeluaran",
|
||||
"placeholder": "Restoran Senin malam",
|
||||
"description": "Masukkan deskripsi untuk pengeluaran."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Tanggal pengeluaran",
|
||||
"description": "Masukkan tanggal pembayaran pengeluaran."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "Mata uang pengeluaran",
|
||||
"description": "Mata uang yang digunakan untuk membayar pengeluaran tersebut."
|
||||
},
|
||||
"categoryFieldDescription": "Pilih kategori pengeluaran.",
|
||||
"paidByField": {
|
||||
"label": "Dibayar oleh",
|
||||
"placeholder": "Pilih peserta",
|
||||
"description": "Pilih peserta yang membayar pengeluaran."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Pengeluaran Berulang",
|
||||
"description": "Pilih seberapa sering pengeluaran harus diulang.",
|
||||
"none": "Tidak ada",
|
||||
"daily": "Setiap hari",
|
||||
"weekly": "Setiap minggu",
|
||||
"monthly": "Setiap bulan"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Dibayar oleh",
|
||||
"description": "Pilih untuk siapa biaya tersebut dibayarkan."
|
||||
},
|
||||
"splitModeDescription": "Pilih cara membagi pengeluaran.",
|
||||
"attachDescription": "Lihat dan lampirkan tanda terima pada pengeluaran."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Jumlah"
|
||||
},
|
||||
"conversionUnavailable": "Untuk menetapkan mata uang yang berbeda per pengeluaran dan mengonversi jumlahnya, pilih mata uang non-kustom untuk grup tersebut.",
|
||||
"originalAmountField": {
|
||||
"label": "Jumlah untuk dikonversi"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Gunakan kurs dari Frankfurter",
|
||||
"useCustom": "Gunakan kurs kustom",
|
||||
"label": "Kurs"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Mendapatkan kurs…",
|
||||
"success": "Kurs yang didapatkan:",
|
||||
"error": "Ups, kami tidak bisa mendapatkan kurs terkini.",
|
||||
"staleRate": "Menggunakan kurs:",
|
||||
"noRate": "Masukkan kurs kustom di bawah ini.",
|
||||
"currencyNotFound": "Ups, Frankfurter tidak memiliki kurs untuk mata uang ini pada hari ini.",
|
||||
"noDate": "Masukkan tanggal pengeluaran untuk mendapatkan kurs.",
|
||||
"dateMismatch": "Kurs pada tanggal: {date}",
|
||||
"refresh": "Muat ulang",
|
||||
"customRate": "Menggunakan kurs kustom"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Ini adalah reimburse"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategori"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Catatan"
|
||||
},
|
||||
"selectNone": "Pilih tidak ada",
|
||||
"selectAll": "Pilih semua",
|
||||
"shares": "porsi",
|
||||
"advancedOptions": "Opsi lanjutan pembagian…",
|
||||
"SplitModeField": {
|
||||
"label": "Mode pembagian",
|
||||
"evenly": "Bagi merata",
|
||||
"byShares": "Tidak merata – Berdasarkan porsi",
|
||||
"byPercentage": "Tidak merata – Berdasarkan persenan",
|
||||
"byAmount": "Tidak merata – Berdasarkan jumlah",
|
||||
"saveAsDefault": "Simpan sebagai opsi pembagian default"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Hapus",
|
||||
"title": "Hapus pengeluaran?",
|
||||
"description": "Apakah Anda yakin ingin menghapus pengeluaran ini? Tindakan ini tidak dapat dibatalkan.",
|
||||
"yes": "Iya",
|
||||
"cancel": "Batalkan"
|
||||
},
|
||||
"attachDocuments": "Lampirkan dokumen",
|
||||
"create": "Buat",
|
||||
"creating": "Membuat…",
|
||||
"save": "Simpan",
|
||||
"saving": "Menyimpan…",
|
||||
"cancel": "Batalkan",
|
||||
"reimbursement": "Reimburse"
|
||||
},
|
||||
"Currencies": {
|
||||
"custom": {
|
||||
"heading": "Kustom"
|
||||
},
|
||||
"search": "Cari mata uang...",
|
||||
"noCurrency": "Tidak ada mata uang yang ditemukan.",
|
||||
"common": {
|
||||
"heading": "Paling umum"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Mata uang lainnya"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Ukuran file terlalu besar",
|
||||
"description": "Ukuran file maksimum yang dapat Anda unggah adalah {maxSize}. Ukuran file Anda adalah {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error saat mengunggah dokumen",
|
||||
"description": "Terjadi kesalahan saat mengunggah dokumen. Silakan coba lagi nanti atau pilih file lain.",
|
||||
"retry": "Ulangi"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Buat pengeluaran dari tanda terima",
|
||||
"title": "Buat dari tanda terima",
|
||||
"description": "Ekstrak informasi pengeluaran dari foto tanda terima.",
|
||||
"body": "Unggah foto struk, dan kami akan memindainya untuk mengekstrak informasi pengeluaran jika memungkinkan.",
|
||||
"selectImage": "Pilih gambar…",
|
||||
"titleLabel": "Judul:",
|
||||
"categoryLabel": "Kategori:",
|
||||
"amountLabel": "Jumlah:",
|
||||
"dateLabel": "Tanggal:",
|
||||
"editNext": "Anda dapat mengedit informasi pengeluaran berikutnya.",
|
||||
"continue": "Lanjutkan"
|
||||
},
|
||||
"unknown": "Tidak diketahui",
|
||||
"TooBigToast": {
|
||||
"title": "File terlalu besar",
|
||||
"description": "Ukuran file maksimum yang dapat Anda unggah adalah {maxSize}. Ukuran file Anda adalah {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Error saat mengunggah dokumen",
|
||||
"description": "Terjadi kesalahan saat mengunggah dokumen. Silakan coba lagi nanti atau pilih file lain.",
|
||||
"retry": "Ulangi"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Saldo",
|
||||
"description": "Ini adalah jumlah yang dibayarkan atau yang diterima oleh setiap peserta.",
|
||||
"Reimbursements": {
|
||||
"title": "Saran reimburse",
|
||||
"description": "Berikut adalah saran untuk reimburse yang optimal antara peserta.",
|
||||
"noImbursements": "Sepertinya grup Anda tidak memerlukan reimburse apa pun 😁",
|
||||
"owes": "<strong>{from}</strong> berutang <strong>{to}</strong>",
|
||||
"markAsPaid": "Tandai sebagai telah dibayar"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistik",
|
||||
"Totals": {
|
||||
"title": "Total",
|
||||
"description": "Ringkasan pengeluaran seluruh grup.",
|
||||
"groupSpendings": "Total pengeluaran grup",
|
||||
"groupEarnings": "Total pemasukan grup",
|
||||
"yourSpendings": "Total pengeluaran Anda",
|
||||
"yourEarnings": "Total pemasukan Anda",
|
||||
"yourShare": "Total bagian Anda"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Aktivitas",
|
||||
"description": "Overview semua aktivitas dalam grup ini.",
|
||||
"noActivity": "Belum ada aktivitas di grup Anda.",
|
||||
"someone": "Seseorang",
|
||||
"settingsModified": "Pengaturan grup diubah oleh <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Pengeluaran <em>{expense}</em> dibuat oleh <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Pengeluaran <em>{expense}</em> diperbarui oleh <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Pengeluaran <em>{expense}</em> dihapus oleh <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Hari ini",
|
||||
"yesterday": "Kemarin",
|
||||
"earlierThisWeek": "Awal minggu ini",
|
||||
"lastWeek": "Minggu lalu",
|
||||
"earlierThisMonth": "Awal bulan ini",
|
||||
"lastMonth": "Bulan lalu",
|
||||
"earlierThisYear": "Awal tahun ini",
|
||||
"lastYear": "Tahun lalu",
|
||||
"older": "Lebih lama"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informasi",
|
||||
"description": "Gunakan tempat ini untuk menambahkan informasi apa pun yang relevan bagi peserta grup.",
|
||||
"empty": "Belum ada informasi grup."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Pengaturan"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Bagikan",
|
||||
"description": "Agar peserta lain dapat melihat grup dan menambahkan pengeluaran, bagikan URL-nya dengan mereka.",
|
||||
"warning": "Peringatan!",
|
||||
"warningHelp": "Setiap orang yang memiliki URL grup akan dapat melihat dan mengedit pengeluaran. Bagikan dengan hati-hati!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Masukkan setidaknya satu karakter.",
|
||||
"min2": "Masukkan setidaknya dua karakter.",
|
||||
"max5": "Masukkan maksimal lima karakter.",
|
||||
"max50": "Masukkan maksimal 50 karakter.",
|
||||
"duplicateParticipantName": "Peserta lain sudah menggunakan nama ini.",
|
||||
"titleRequired": "Silakan masukkan judul.",
|
||||
"invalidNumber": "Nomor tidak valid.",
|
||||
"amountRequired": "Anda harus memasukkan jumlah.",
|
||||
"amountNotZero": "Jumlahnya tidak boleh nol.",
|
||||
"amountTenMillion": "Jumlahnya harus kurang dari 10.000.000.",
|
||||
"ratePositive": "Angkanya harus lebih besar dari nol.",
|
||||
"paidByRequired": "Anda harus memilih peserta.",
|
||||
"paidForMin1": "Pengeluaran harus dibayarkan untuk setidaknya satu peserta.",
|
||||
"noZeroShares": "Semua porsi harus lebih tinggi dari 0.",
|
||||
"amountSum": "Total harus sama dengan jumlah pengeluaran.",
|
||||
"percentageSum": "Total persentase harus sama dengan 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Cari kategori...",
|
||||
"noCategory": "Tidak ada kategori yang ditemukan.",
|
||||
"Uncategorized": {
|
||||
"heading": "Tidak Berkategori",
|
||||
"General": "Umum",
|
||||
"Payment": "Pembayaran"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Hiburan",
|
||||
"Entertainment": "Hiburan",
|
||||
"Games": "Game",
|
||||
"Movies": "Film",
|
||||
"Music": "Musik",
|
||||
"Sports": "Olah Raga"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Makanan dan Minuman",
|
||||
"Food and Drink": "Makanan dan Minuman",
|
||||
"Dining Out": "Makan luar",
|
||||
"Groceries": "Belanja",
|
||||
"Liquor": "Minuman Keras"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Rumah",
|
||||
"Home": "Rumah",
|
||||
"Electronics": "Elektronik",
|
||||
"Furniture": "Furnitur",
|
||||
"Household Supplies": "Kebutuhan rumah",
|
||||
"Maintenance": "Perawatan rumah",
|
||||
"Mortgage": "Cicilan",
|
||||
"Pets": "Binatang peliharaan",
|
||||
"Rent": "Sewa",
|
||||
"Services": "Servis"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Kehidupan",
|
||||
"Childcare": "Anak-anak",
|
||||
"Clothing": "Pakaian",
|
||||
"Donation": "Donasi",
|
||||
"Education": "Edukasi",
|
||||
"Gifts": "Hadiah",
|
||||
"Insurance": "Asuransi",
|
||||
"Medical Expenses": "Kesehatan",
|
||||
"Taxes": "Pajak"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transportasi",
|
||||
"Transportation": "Transpotrasi",
|
||||
"Bicycle": "Sepeda",
|
||||
"Bus/Train": "Bis/Kereta",
|
||||
"Car": "Mobil",
|
||||
"Gas/Fuel": "Bensin",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parkir",
|
||||
"Plane": "Pesawat",
|
||||
"Taxi": "Taksi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilitas",
|
||||
"Utilities": "Utilitas",
|
||||
"Cleaning": "Kebersihan",
|
||||
"Electricity": "Listrik",
|
||||
"Heat/Gas": "Gas",
|
||||
"Trash": "Sampah",
|
||||
"TV/Phone/Internet": "TV/HP/Internet",
|
||||
"Water": "Air"
|
||||
}
|
||||
}
|
||||
}
|
||||
413
messages/it-IT.json
Normal file
@@ -0,0 +1,413 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Condividi <strong>Spese</strong> con <strong>Amici & Familiari</strong>",
|
||||
"description": "Benvenuto nella tua nuova installazione di <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Vai ai gruppi",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Gruppi"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Realizzato a Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Sviluppato da <author>Sebastien Castiel</author> e <source>contributori</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Spese",
|
||||
"description": "Ecco le spese che hai creato per il tuo gruppo.",
|
||||
"create": "Crea spesa",
|
||||
"createFirst": "Crea la prima",
|
||||
"noExpenses": "Il tuo gruppo non contiene ancora spese.",
|
||||
"exportJson": "Esporta file JSON",
|
||||
"exportCsv": "Esporta file CSV",
|
||||
"searchPlaceholder": "Cerca una spesa…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Chi sei?",
|
||||
"description": "Dicci quale partecipante sei per consentirci di personalizzare la modalità di visualizzazione delle informazioni.",
|
||||
"nobody": "Non voglio selezionare nessuno",
|
||||
"save": "Salva cambiamenti",
|
||||
"footer": "Questa impostazione può essere modificata successivamente nelle impostazioni del gruppo."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "In arrivo",
|
||||
"thisWeek": "Questa settimana",
|
||||
"earlierThisMonth": "All'inizio di questo mese",
|
||||
"lastMonth": "Ultimo mese",
|
||||
"earlierThisYear": "All'inizio di quest'anno",
|
||||
"lastYear": "Ultimo anno",
|
||||
"older": "Più vecchio"
|
||||
},
|
||||
"export": "Esporta"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Pagato da <strong>{paidBy}</strong> per <paidFor></paidFor>",
|
||||
"receivedBy": "Ricevuto da <strong>{paidBy}</strong> per <paidFor></paidFor>",
|
||||
"yourBalance": "Il tuo saldo:",
|
||||
"notInvolved": "Non sei coinvolto",
|
||||
"everyone": "tutti"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "I miei gruppi",
|
||||
"create": "Crea",
|
||||
"loadingRecent": "Caricamento gruppi recenti…",
|
||||
"NoRecent": {
|
||||
"description": "Non hai visitato nessun gruppo di recente.",
|
||||
"create": "Creane uno",
|
||||
"orAsk": "oppure chiedi a un amico di inviarti il link a uno esistente."
|
||||
},
|
||||
"recent": "Gruppi recenti",
|
||||
"starred": "Gruppi speciali",
|
||||
"archived": "Gruppi archiviati",
|
||||
"archive": "Archivia gruppo",
|
||||
"unarchive": "Rimuovi il gruppo dall'archivio",
|
||||
"removeRecent": "Rimuovi dai gruppi recenti",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Il gruppo è stato rimosso",
|
||||
"description": "Il gruppo è stato rimosso dall'elenco dei gruppi recenti.",
|
||||
"undoAlt": "Annulla la rimozione del gruppo",
|
||||
"undo": "Annulla"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Aggiungi tramite URL",
|
||||
"title": "Aggiungi un gruppo tramite URL",
|
||||
"description": "Se un gruppo è stato condiviso con te, puoi incollare qui il suo URL per aggiungerlo al tuo elenco.",
|
||||
"error": "Oops, non siamo riusciti a trovare il gruppo dall'URL che hai fornito…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Questo gruppo non esiste.",
|
||||
"link": "Vai ai gruppi visitati di recente"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informazioni del gruppo",
|
||||
"NameField": {
|
||||
"label": "Nome del gruppo",
|
||||
"placeholder": "Vacanze estive",
|
||||
"description": "Inserisci il nome del gruppo."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informazioni del gruppo",
|
||||
"placeholder": "Quali informazioni sono rilevanti per i partecipanti al gruppo?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Simbolo valuta",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Lo useremo per visualizzare gli importi."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Partecipanti",
|
||||
"description": "Immettere il nome per ciascun partecipante.",
|
||||
"protectedParticipant": "Questo partecipante fa parte delle spese e non può essere rimosso.",
|
||||
"new": "Nuovo",
|
||||
"add": "Aggiungi partecipante",
|
||||
"John": "Fabio",
|
||||
"Jane": "Kaneda",
|
||||
"Jack": "Albano"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Impostazioni locali",
|
||||
"description": "Queste impostazioni sono impostate per dispositivo e vengono utilizzate per personalizzare la tua esperienza.",
|
||||
"ActiveUserField": {
|
||||
"label": "Utente attivo",
|
||||
"placeholder": "Seleziona un partecipante",
|
||||
"none": "Nessuno",
|
||||
"description": "Utente utilizzato come predefinito per il pagamento delle spese."
|
||||
},
|
||||
"save": "Salva",
|
||||
"saving": "Salvataggio…",
|
||||
"create": "Crea",
|
||||
"creating": "Sto creando…",
|
||||
"cancel": "Annulla"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Crea entrata",
|
||||
"edit": "Modifica entrata",
|
||||
"TitleField": {
|
||||
"label": "Titolo entrata",
|
||||
"placeholder": "Ristorante del lunedì sera",
|
||||
"description": "Inserisci una descrizione per l'entrata."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data entrata",
|
||||
"description": "Inserisci la data in cui è stato ricevuta l'entrata."
|
||||
},
|
||||
"categoryFieldDescription": "Seleziona la categoria dell'entrata.",
|
||||
"paidByField": {
|
||||
"label": "Ricevuta da",
|
||||
"description": "Seleziona il partecipante che ha ricevuto l'entrata."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Spesa ricorrente",
|
||||
"description": "Seleziona quanto spesso deve ripetersi.",
|
||||
"none": "Mai",
|
||||
"daily": "Giornaliera",
|
||||
"weekly": "Settimanale",
|
||||
"monthly": "Mensile"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Ricevuto per",
|
||||
"description": "Seleziona per chi è stato ricevuto il reddito."
|
||||
},
|
||||
"splitModeDescription": "Seleziona come dividere l'entrata.",
|
||||
"attachDescription": "Vedi ed allega la ricevuta per l'entrata."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Crea spesa",
|
||||
"edit": "Edita spesa",
|
||||
"TitleField": {
|
||||
"label": "Titolo Spesa",
|
||||
"placeholder": "Ristorante del lunedì sera",
|
||||
"description": "Inserisci una descrizione per la spesa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data spesa",
|
||||
"description": "Inserisci la data in cui si è svolta la spesa."
|
||||
},
|
||||
"categoryFieldDescription": "Seleziona la categoria della spesa.",
|
||||
"paidByField": {
|
||||
"label": "Pagato da",
|
||||
"description": "Seleziona il partecipante che ha pagato la spesa.",
|
||||
"placeholder": "Seleziona un partecipante"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Spesa ricorrente",
|
||||
"description": "Seleziona quanto spesso deve ripetersi.",
|
||||
"none": "Mai",
|
||||
"daily": "Giornaliera",
|
||||
"weekly": "Settimanale",
|
||||
"monthly": "Mensile"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Pagato per",
|
||||
"description": "Seleleziona per chi è stato pagato."
|
||||
},
|
||||
"splitModeDescription": "Seleziona come dividere la spesa.",
|
||||
"attachDescription": "Vedi ed allega la ricevuta per la spesa."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Importo"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Questo è un rimborso"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Note"
|
||||
},
|
||||
"selectNone": "Seleziona nessuna",
|
||||
"selectAll": "Seleziona tutto",
|
||||
"shares": "condividi",
|
||||
"advancedOptions": "Opzioni di divisione avanzate…",
|
||||
"SplitModeField": {
|
||||
"label": "Modalità split",
|
||||
"evenly": "Uniforme",
|
||||
"byShares": "Non uniforme – Per quote",
|
||||
"byPercentage": "Non uniforme – Per percentuale",
|
||||
"byAmount": "Non uniforme – Per importo",
|
||||
"saveAsDefault": "Salva come opzione di suddivisione predefinita"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Rimuovi",
|
||||
"title": "Rimuovere questa spesa?",
|
||||
"description": "Vuoi davvero eliminare questa spesa? Questa azione è irreversibile.",
|
||||
"yes": "Si",
|
||||
"cancel": "Annulla"
|
||||
},
|
||||
"attachDocuments": "Documenti allegati",
|
||||
"create": "Crea",
|
||||
"creating": "Sto creando…",
|
||||
"save": "Salva",
|
||||
"saving": "Sto salvando…",
|
||||
"cancel": "Annulla",
|
||||
"reimbursement": "Rimborso",
|
||||
"conversionRateState": {
|
||||
"refresh": "Aggiornare"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Il file è troppo grande",
|
||||
"description": "La dimensione massima del file che puoi caricare è {maxSize}. Il tuo è {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Errore durante il caricamento del documento",
|
||||
"description": "Si è verificato un errore durante il caricamento del documento. Riprova più tardi o seleziona un file diverso.",
|
||||
"retry": "Riprova"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Crea spesa dalla ricevuta",
|
||||
"title": "Crea dalla ricevuta",
|
||||
"description": "Estrai le informazioni sulla spesa da una foto della ricevuta.",
|
||||
"body": "Carica la foto di una ricevuta e, se possibile, la scannerizzeremo per estrarre le informazioni sulle spese.",
|
||||
"selectImage": "Seleziona immagine…",
|
||||
"titleLabel": "Titolo:",
|
||||
"categoryLabel": "Categoria:",
|
||||
"amountLabel": "Importo:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "Successivamente potrai modificare le informazioni sulle spese.",
|
||||
"continue": "Continua"
|
||||
},
|
||||
"unknown": "Sconosciuto",
|
||||
"TooBigToast": {
|
||||
"title": "Il file è troppo grande",
|
||||
"description": "La dimensione massima del file che puoi caricare è {maxSize}. Il tuo è {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Errore durante il caricamento del documento",
|
||||
"description": "Si è verificato un errore durante il caricamento del documento. Riprova più tardi o seleziona un file diverso.",
|
||||
"retry": "Riprova"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Bilanci",
|
||||
"description": "Questo è l'importo che ciascun partecipante ha pagato o deve pagare.",
|
||||
"Reimbursements": {
|
||||
"title": "Rimborsi suggeriti",
|
||||
"description": "Ecco alcuni suggerimenti per ottimizzare i rimborsi tra i partecipanti.",
|
||||
"noImbursements": "Sembra che il tuo gruppo non abbia bisogno di alcun rimborso 😁",
|
||||
"owes": "<strong>{from}</strong> deve <strong>{to}</strong>",
|
||||
"markAsPaid": "Segna come pagato"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistiche",
|
||||
"Totals": {
|
||||
"title": "Totali",
|
||||
"description": "Riepilogo delle spese dell'intero gruppo.",
|
||||
"groupSpendings": "Spese totali del gruppo",
|
||||
"groupEarnings": "Guadagno totale del gruppo",
|
||||
"yourSpendings": "Le tue spese totali",
|
||||
"yourEarnings": "I tuoi guadagni totali",
|
||||
"yourShare": "La tua quota totale"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Attività",
|
||||
"description": "Panoramica di tutte le attività in questo gruppo.",
|
||||
"noActivity": "Non c'è ancora alcuna attività nel tuo gruppo.",
|
||||
"someone": "Qualcuno",
|
||||
"settingsModified": "Le impostazioni del gruppo sono state modificate da <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Spesa <em>{expense}</em> creata da <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Spesa <em>{expense}</em> aggiornata da <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Spesa <em>{expense}</em> cancellata da <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Oggi",
|
||||
"yesterday": "Ieri",
|
||||
"earlierThisWeek": "All'inizio di questa settimana",
|
||||
"lastWeek": "La settimana scorsa",
|
||||
"earlierThisMonth": "All'inizio di questo mese",
|
||||
"lastMonth": "Lo scorso mese",
|
||||
"earlierThisYear": "All'inizio di questo anno",
|
||||
"lastYear": "Lo scorso anno",
|
||||
"older": "Più vecchio"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informazioni",
|
||||
"description": "Utilizza questo posto per aggiungere qualsiasi informazione che possa essere rilevante per i partecipanti al gruppo.",
|
||||
"empty": "Ancora nessuna informazione sul gruppo."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Impostazioni"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Condividi",
|
||||
"description": "Per consentire agli altri partecipanti di vedere il gruppo e aggiungere spese, condividi il suo URL con loro.",
|
||||
"warning": "Attenzione!",
|
||||
"warningHelp": "Ogni persona con l'URL del gruppo potrà vedere e modificare le spese. Condividi con cautela!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Inserisci almeno un carattere.",
|
||||
"min2": "Inserisci almeno due caratteri.",
|
||||
"max5": "Inserisci al massimo cinque caratteri.",
|
||||
"max50": "Inserisci al massimo cinquanta caratteri.",
|
||||
"duplicateParticipantName": "Un altro partecipante ha già questo nome.",
|
||||
"titleRequired": "Inserisci un titolo.",
|
||||
"invalidNumber": "Numero invalido.",
|
||||
"amountRequired": "Devi inserire un importo.",
|
||||
"amountNotZero": "L'importo non deve essere zero.",
|
||||
"amountTenMillion": "L'importo deve essere inferiore a 10.000.000.",
|
||||
"paidByRequired": "È necessario selezionare un partecipante.",
|
||||
"paidForMin1": "La spesa deve essere pagata per almeno un partecipante.",
|
||||
"noZeroShares": "Tutti gli importi devono essere superiori a 0.",
|
||||
"amountSum": "La somma degli importi deve essere uguale all'importo della spesa.",
|
||||
"percentageSum": "La somma delle percentuali deve essere uguale a 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Cerca categoria...",
|
||||
"noCategory": "Nessuna categoria trovata.",
|
||||
"Uncategorized": {
|
||||
"heading": "Senza categoria",
|
||||
"General": "Generale",
|
||||
"Payment": "Pagamento"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Intrattenimento",
|
||||
"Entertainment": "Intrattenimento",
|
||||
"Games": "Giochi",
|
||||
"Movies": "Film",
|
||||
"Music": "Musica",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Cibo e Bevande",
|
||||
"Food and Drink": "Cibo e Bevande",
|
||||
"Dining Out": "Mangiare fuori",
|
||||
"Groceries": "Generi alimentari",
|
||||
"Liquor": "Liquori"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Casa",
|
||||
"Home": "Casa",
|
||||
"Electronics": "Elettronica di consumo",
|
||||
"Furniture": "Mobili",
|
||||
"Household Supplies": "Prodotti per la casa",
|
||||
"Maintenance": "Manutenzione",
|
||||
"Mortgage": "Mutuo",
|
||||
"Pets": "Animali",
|
||||
"Rent": "Affitti",
|
||||
"Services": "Servizi"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Vita",
|
||||
"Childcare": "Cura dei bambini",
|
||||
"Clothing": "Abbigliamento",
|
||||
"Donation": "Donazioni",
|
||||
"Education": "Istruzione",
|
||||
"Gifts": "Regali",
|
||||
"Insurance": "Assicurazioni",
|
||||
"Medical Expenses": "Spese Mediche",
|
||||
"Taxes": "Tasse"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Trasporti",
|
||||
"Transportation": "Trasporti",
|
||||
"Bicycle": "Bicicletta",
|
||||
"Bus/Train": "Bus/Treno",
|
||||
"Car": "Auto",
|
||||
"Gas/Fuel": "Gas/Carburante",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parcheggio",
|
||||
"Plane": "Aereo",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utenze",
|
||||
"Utilities": "Utenze",
|
||||
"Cleaning": "Pulizie",
|
||||
"Electricity": "Elettricità",
|
||||
"Heat/Gas": "Riscaldamento/Gas",
|
||||
"Trash": "Rifiuti",
|
||||
"TV/Phone/Internet": "TV/Telefono/Internet",
|
||||
"Water": "Acqua"
|
||||
}
|
||||
}
|
||||
}
|
||||
451
messages/ja-JP.json
Normal file
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "友達や家族と<strong>費用</strong>を<strong>分担</strong>しよう",
|
||||
"description": "新しい<strong>Spliit</strong>インスタンスへようこそ!",
|
||||
"button": {
|
||||
"groups": "グループへ移動",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "グループ"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "モントリオール、ケベック 🇨🇦 で製作",
|
||||
"builtBy": "<author>Sebastien Castiel</author>と<source>貢献者</source>によって構築"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "支出",
|
||||
"description": "グループのために作成した支出はこちらです。",
|
||||
"create": "支出を作成",
|
||||
"createFirst": "最初の支出を作成",
|
||||
"noExpenses": "グループにはまだ支出がありません。",
|
||||
"export": "エクスポート",
|
||||
"exportJson": "JSONにエクスポート",
|
||||
"exportCsv": "CSVにエクスポート",
|
||||
"searchPlaceholder": "支出を検索…",
|
||||
"ActiveUserModal": {
|
||||
"title": "あなたは誰ですか?",
|
||||
"description": "情報の表示方法をカスタマイズするために、あなたがどの参加者かを教えてください。",
|
||||
"nobody": "誰も選択したくありません",
|
||||
"save": "変更を保存",
|
||||
"footer": "この設定は後でグループ設定で変更できます。"
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "今後",
|
||||
"thisWeek": "今週",
|
||||
"earlierThisMonth": "今月初め",
|
||||
"lastMonth": "先月",
|
||||
"earlierThisYear": "今年初め",
|
||||
"lastYear": "昨年",
|
||||
"older": "それ以前"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong>が<paidFor></paidFor>のために支払いました",
|
||||
"receivedBy": "<strong>{paidBy}</strong>が<paidFor></paidFor>のために受け取りました",
|
||||
"yourBalance": "あなたの残高:",
|
||||
"everyone": "全員",
|
||||
"notInvolved": "あなたは関係していません"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "マイグループ",
|
||||
"create": "作成",
|
||||
"loadingRecent": "最近のグループをロード中…",
|
||||
"NoRecent": {
|
||||
"description": "最近訪れたグループはありません。",
|
||||
"create": "グループを作成する",
|
||||
"orAsk": "または友達に既存のグループへのリンクを送ってもらいましょう。"
|
||||
},
|
||||
"recent": "最近のグループ",
|
||||
"starred": "スター付きグループ",
|
||||
"archived": "アーカイブ済みグループ",
|
||||
"archive": "グループをアーカイブ",
|
||||
"unarchive": "グループのアーカイブを解除",
|
||||
"removeRecent": "最近のグループから削除",
|
||||
"RecentRemovedToast": {
|
||||
"title": "グループが削除されました",
|
||||
"description": "グループは最近のグループリストから削除されました。",
|
||||
"undoAlt": "削除を元に戻す",
|
||||
"undo": "元に戻す"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "URLで追加",
|
||||
"title": "URLでグループを追加",
|
||||
"description": "グループが共有された場合、そのURLをここに貼り付けてリストに追加できます。",
|
||||
"error": "あら、提供されたURLからグループを見つけることができませんでした…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "このグループは存在しません。",
|
||||
"link": "最近訪れたグループへ移動"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "グループ情報",
|
||||
"NameField": {
|
||||
"label": "グループ名",
|
||||
"placeholder": "夏休み",
|
||||
"description": "グループの名前を入力してください。"
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "グループ情報",
|
||||
"placeholder": "グループ参加者に関連する情報は何ですか?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "通貨記号",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "金額の表示に使用します。"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "参加者",
|
||||
"description": "各参加者の名前を入力してください。",
|
||||
"protectedParticipant": "この参加者は支出の一部であり、削除できません。",
|
||||
"new": "新規",
|
||||
"add": "参加者を追加",
|
||||
"John": "一郎",
|
||||
"Jane": "花子",
|
||||
"Jack": "太郎"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "ローカル設定",
|
||||
"description": "これらの設定はデバイスごとに設定され、あなたの体験をカスタマイズするために使用されます。",
|
||||
"ActiveUserField": {
|
||||
"label": "アクティブユーザー",
|
||||
"placeholder": "参加者を選択",
|
||||
"none": "なし",
|
||||
"description": "支出の支払いのデフォルトとして使用されるユーザー。"
|
||||
},
|
||||
"save": "保存",
|
||||
"saving": "保存中…",
|
||||
"create": "作成",
|
||||
"creating": "作成中…",
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "主な通貨",
|
||||
"createDescription": "すべての金額および残高はこの通貨で表示されます。",
|
||||
"editDescription": "すべての金額および残高はこの通貨で表示されます。これを変更しても、既に入力された支出は変換されません。ただし、現在の通貨と変更先の通貨で小数単位(例:米ドルから日本円へ)の扱いが異なる場合は例外です",
|
||||
"customOption": "カスタム"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "収入を作成",
|
||||
"edit": "収入を編集",
|
||||
"TitleField": {
|
||||
"label": "収入タイトル",
|
||||
"placeholder": "月曜日の夕食レストラン",
|
||||
"description": "収入の説明を入力してください。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "収入日",
|
||||
"description": "収入を受け取った日付を入力してください。"
|
||||
},
|
||||
"categoryFieldDescription": "収入カテゴリーを選択してください。",
|
||||
"paidByField": {
|
||||
"label": "受取人",
|
||||
"description": "収入を受け取った参加者を選択してください。"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "受け取り対象",
|
||||
"description": "誰のために収入が受け取られたかを選択してください。"
|
||||
},
|
||||
"splitModeDescription": "収入の分割方法を選択してください。",
|
||||
"attachDescription": "領収書を確認し、収入に添付してください。",
|
||||
"currencyField": {
|
||||
"label": "収入の通貨",
|
||||
"description": "収入が受け取られた通貨。"
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "支出を作成",
|
||||
"edit": "支出を編集",
|
||||
"TitleField": {
|
||||
"label": "支出タイトル",
|
||||
"placeholder": "月曜日の夕食レストラン",
|
||||
"description": "支出の説明を入力してください。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "支出日",
|
||||
"description": "支出が支払われた日付を入力してください。"
|
||||
},
|
||||
"categoryFieldDescription": "支出カテゴリーを選択してください。",
|
||||
"paidByField": {
|
||||
"label": "支払者",
|
||||
"description": "支出を支払った参加者を選択してください。",
|
||||
"placeholder": "参加者を選択してください"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "支出の繰り返し",
|
||||
"description": "支出の繰り返し頻度を選択してください。",
|
||||
"none": "なし",
|
||||
"daily": "毎日",
|
||||
"weekly": "毎週",
|
||||
"monthly": "毎月"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "支払い対象",
|
||||
"description": "誰のために支出が支払われたかを選択してください。"
|
||||
},
|
||||
"splitModeDescription": "支出の分割方法を選択してください。",
|
||||
"attachDescription": "領収書を確認し、支出に添付してください。",
|
||||
"currencyField": {
|
||||
"label": "支出の通貨",
|
||||
"description": "支出が支払われた通貨。"
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "金額"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "これは払い戻しです"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "カテゴリー"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "メモ"
|
||||
},
|
||||
"selectNone": "選択解除",
|
||||
"selectAll": "すべて選択",
|
||||
"shares": "シェア",
|
||||
"advancedOptions": "詳細な分割オプション…",
|
||||
"SplitModeField": {
|
||||
"label": "分割モード",
|
||||
"evenly": "均等に",
|
||||
"byShares": "不均等に - シェアで",
|
||||
"byPercentage": "不均等に - パーセンテージで",
|
||||
"byAmount": "不均等に - 金額で",
|
||||
"saveAsDefault": "デフォルトの分割オプションとして保存"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "削除",
|
||||
"title": "この支出を削除しますか?",
|
||||
"description": "本当にこの支出を削除しますか?この操作は元に戻せません。",
|
||||
"yes": "はい",
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"attachDocuments": "書類を添付",
|
||||
"create": "作成",
|
||||
"creating": "作成中…",
|
||||
"save": "保存",
|
||||
"saving": "保存中…",
|
||||
"cancel": "キャンセル",
|
||||
"reimbursement": "払い戻し",
|
||||
"conversionUnavailable": "支出ごとに異なる通貨を設定して金額を換算するには、グループの通貨をカスタム以外の通貨に設定してください。",
|
||||
"originalAmountField": {
|
||||
"label": "換算する金額"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Frankfurterのレートを使用する",
|
||||
"useCustom": "カスタムレートを使用する",
|
||||
"label": "為替レート"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "為替レートを取得しています…",
|
||||
"success": "取得したレート:",
|
||||
"error": "おっと、最新のレートを取得できませんでした。",
|
||||
"staleRate": "使用するレート:",
|
||||
"noRate": "以下にカスタムレートを入力してください。",
|
||||
"currencyNotFound": "おっと、Frankfurterにはその日のこの通貨のレートがありません。",
|
||||
"noDate": "換算レートを取得するには支出日を入力してください。",
|
||||
"refresh": "更新",
|
||||
"customRate": "カスタムレートを使用中",
|
||||
"dateMismatch": "適用開始日: {date}"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "ファイルが大きすぎます",
|
||||
"description": "アップロードできる最大ファイルサイズは{maxSize}です。あなたのファイルは{size}です。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "書類のアップロード中にエラーが発生しました",
|
||||
"description": "書類のアップロード中に何か問題が発生しました。後で再試行するか、別のファイルを選択してください。",
|
||||
"retry": "再試行"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "領収書から支出を作成",
|
||||
"title": "領収書から作成",
|
||||
"description": "領収書の写真から支出情報を抽出します。",
|
||||
"body": "領収書の写真をアップロードすると、可能であれば支出情報を抽出してスキャンします。",
|
||||
"selectImage": "画像を選択…",
|
||||
"titleLabel": "タイトル:",
|
||||
"categoryLabel": "カテゴリー:",
|
||||
"amountLabel": "金額:",
|
||||
"dateLabel": "日付:",
|
||||
"editNext": "次に支出情報を編集できます。",
|
||||
"continue": "続ける"
|
||||
},
|
||||
"unknown": "不明",
|
||||
"TooBigToast": {
|
||||
"title": "ファイルが大きすぎます",
|
||||
"description": "アップロードできる最大ファイルサイズは{maxSize}です。あなたのファイルは{size}です。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "書類のアップロード中にエラーが発生しました",
|
||||
"description": "書類のアップロード中に何か問題が発生しました。後で再試行するか、別のファイルを選択してください。",
|
||||
"retry": "再試行"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "残高",
|
||||
"description": "これは各参加者が支払ったまたは支払われた金額です。",
|
||||
"Reimbursements": {
|
||||
"title": "提案される払い戻し",
|
||||
"description": "参加者間の最適化された払い戻しの提案です。",
|
||||
"noImbursements": "グループに払い戻しが必要ないようです 😁",
|
||||
"owes": "<strong>{from}</strong>は<strong>{to}</strong>に借りがあります",
|
||||
"markAsPaid": "支払い済みとしてマーク"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "統計",
|
||||
"Totals": {
|
||||
"title": "合計",
|
||||
"description": "グループ全体の支出概要。",
|
||||
"groupSpendings": "グループ総支出",
|
||||
"groupEarnings": "グループ総収入",
|
||||
"yourSpendings": "あなたの総支出",
|
||||
"yourEarnings": "あなたの総収入",
|
||||
"yourShare": "あなたの総シェア"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "アクティビティ",
|
||||
"description": "このグループのすべてのアクティビティの概要。",
|
||||
"noActivity": "グループにはまだアクティビティがありません。",
|
||||
"someone": "誰か",
|
||||
"settingsModified": "グループ設定が<strong>{participant}</strong>によって変更されました。",
|
||||
"expenseCreated": "支出<em>{expense}</em>が<strong>{participant}</strong>によって作成されました。",
|
||||
"expenseUpdated": "支出<em>{expense}</em>が<strong>{participant}</strong>によって更新されました。",
|
||||
"expenseDeleted": "支出<em>{expense}</em>が<strong>{participant}</strong>によって削除されました。",
|
||||
"Groups": {
|
||||
"today": "今日",
|
||||
"yesterday": "昨日",
|
||||
"earlierThisWeek": "今週初め",
|
||||
"lastWeek": "先週",
|
||||
"earlierThisMonth": "今月初め",
|
||||
"lastMonth": "先月",
|
||||
"earlierThisYear": "今年初め",
|
||||
"lastYear": "昨年",
|
||||
"older": "それ以前"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "情報",
|
||||
"description": "グループ参加者に関連する情報を追加するためにこの場所を使用してください。",
|
||||
"empty": "まだグループ情報がありません。"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "設定"
|
||||
},
|
||||
"Share": {
|
||||
"title": "共有",
|
||||
"description": "他の参加者がグループを見て支出を追加できるように、グループのURLを共有してください。",
|
||||
"warning": "警告!",
|
||||
"warningHelp": "グループURLを持つすべての人が支出を閲覧および編集できるようになります。注意して共有してください!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "少なくとも1文字入力してください。",
|
||||
"min2": "少なくとも2文字入力してください。",
|
||||
"max5": "最大5文字まで入力してください。",
|
||||
"max50": "最大50文字まで入力してください。",
|
||||
"duplicateParticipantName": "別の参加者がすでにこの名前を使用しています。",
|
||||
"titleRequired": "タイトルを入力してください。",
|
||||
"invalidNumber": "無効な数字です。",
|
||||
"amountRequired": "金額を入力する必要があります。",
|
||||
"amountNotZero": "金額はゼロであってはなりません。",
|
||||
"amountTenMillion": "金額は10,000,000未満である必要があります。",
|
||||
"paidByRequired": "参加者を選択する必要があります。",
|
||||
"paidForMin1": "支出は少なくとも1人の参加者のために支払われている必要があります。",
|
||||
"noZeroShares": "すべてのシェアは0より大きくなければなりません。",
|
||||
"amountSum": "金額の合計は支出金額と一致する必要があります。",
|
||||
"percentageSum": "パーセンテージの合計は100である必要があります。",
|
||||
"ratePositive": "レートは必ずゼロより大きくなければなりません。"
|
||||
},
|
||||
"Categories": {
|
||||
"search": "カテゴリーを検索...",
|
||||
"noCategory": "カテゴリーが見つかりません。",
|
||||
"Uncategorized": {
|
||||
"heading": "未分類",
|
||||
"General": "一般",
|
||||
"Payment": "支払い"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "エンターテイメント",
|
||||
"Entertainment": "エンターテイメント",
|
||||
"Games": "ゲーム",
|
||||
"Movies": "映画",
|
||||
"Music": "音楽",
|
||||
"Sports": "スポーツ"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "食事と飲み物",
|
||||
"Food and Drink": "食事と飲み物",
|
||||
"Dining Out": "外食",
|
||||
"Groceries": "食料品",
|
||||
"Liquor": "酒類"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "住まい",
|
||||
"Home": "住まい",
|
||||
"Electronics": "電子機器",
|
||||
"Furniture": "家具",
|
||||
"Household Supplies": "家庭用品",
|
||||
"Maintenance": "メンテナンス",
|
||||
"Mortgage": "住宅ローン",
|
||||
"Pets": "ペット",
|
||||
"Rent": "家賃",
|
||||
"Services": "サービス"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "生活",
|
||||
"Childcare": "育児",
|
||||
"Clothing": "衣類",
|
||||
"Donation": "寄付",
|
||||
"Education": "教育",
|
||||
"Gifts": "贈り物",
|
||||
"Insurance": "保険",
|
||||
"Medical Expenses": "医療費",
|
||||
"Taxes": "税金"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "交通",
|
||||
"Transportation": "交通",
|
||||
"Bicycle": "自転車",
|
||||
"Bus/Train": "バス/電車",
|
||||
"Car": "車",
|
||||
"Gas/Fuel": "ガソリン/燃料",
|
||||
"Hotel": "ホテル",
|
||||
"Parking": "駐車場",
|
||||
"Plane": "飛行機",
|
||||
"Taxi": "タクシー"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "公共料金",
|
||||
"Utilities": "公共料金",
|
||||
"Cleaning": "清掃",
|
||||
"Electricity": "電気",
|
||||
"Heat/Gas": "暖房/ガス",
|
||||
"Trash": "ゴミ",
|
||||
"TV/Phone/Internet": "テレビ/電話/インターネット",
|
||||
"Water": "水道"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "通貨を検索…",
|
||||
"noCurrency": "通貨が見つかりませんでした。",
|
||||
"custom": {
|
||||
"heading": "カスタム"
|
||||
},
|
||||
"common": {
|
||||
"heading": "一般的なもの"
|
||||
},
|
||||
"other": {
|
||||
"heading": "その他の通貨"
|
||||
}
|
||||
}
|
||||
}
|
||||
388
messages/ko.json
Normal file
@@ -0,0 +1,388 @@
|
||||
{
|
||||
"Expenses": {
|
||||
"exportJson": "JSON으로 내보내기",
|
||||
"exportCsv": "CSV로 내보내기",
|
||||
"export": "내보내기",
|
||||
"ActiveUserModal": {
|
||||
"save": "변경내용 저장하기",
|
||||
"title": "당신은 누구인가요?",
|
||||
"description": "당신이 누구인지 알려 주시면, 정보를 더 잘 보여드릴게요.",
|
||||
"nobody": "아무도 선택하지 않을래요",
|
||||
"footer": "이건 나중에 그룹 설정에서 바꿀 수 있어요."
|
||||
},
|
||||
"Groups": {
|
||||
"thisWeek": "이번주",
|
||||
"earlierThisMonth": "이번달 초",
|
||||
"lastMonth": "지난달",
|
||||
"lastYear": "작년",
|
||||
"earlierThisYear": "올해 초",
|
||||
"older": "이전"
|
||||
},
|
||||
"title": "지출",
|
||||
"description": "그룹에서 만든 지출 항목들을 보여드려요.",
|
||||
"create": "지출 추가하기",
|
||||
"noExpenses": "그룹에 아직 지출 항목이 없네요.",
|
||||
"searchPlaceholder": "지출 검색하기…",
|
||||
"createFirst": "첫 지출 추가하기"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "내 그룹",
|
||||
"create": "만들기",
|
||||
"loadingRecent": "최신 그룹 불러오는 중…",
|
||||
"NoRecent": {
|
||||
"description": "최근에 참여한 그룹이 없습니다.",
|
||||
"orAsk": "또는 친구에게 기존 그룹 링크를 보내 달라고 요청하세요.",
|
||||
"create": "그룹 만들기"
|
||||
},
|
||||
"recent": "최근 그룹",
|
||||
"starred": "즐겨찾기 그룹",
|
||||
"archived": "아카이브된 그룹",
|
||||
"archive": "그룹 아카이브하기",
|
||||
"RecentRemovedToast": {
|
||||
"title": "그룹이 삭제되었습니다",
|
||||
"description": "그룹이 최신 그룹 목록에서 삭제되었습니다.",
|
||||
"undoAlt": "그룹 삭제 취소"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "URL로 불러오기",
|
||||
"title": "URL로 그룹 불러오기",
|
||||
"description": "이미 공유받은 그룹이 있다면, 여기에 URL을 붙여넣어 추가하세요.",
|
||||
"error": "헉, 입력하신 URL에서 그룹을 찾을 수 없네요…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "그룹이 존재하지 않습니다.",
|
||||
"link": "최근에 방문한 그룹 보기"
|
||||
},
|
||||
"unarchive": "그룹 다시 불러오기",
|
||||
"removeRecent": "최근 그룹 목록에서 삭제하기"
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "그룹 정보",
|
||||
"NameField": {
|
||||
"label": "그룹 이름",
|
||||
"placeholder": "여름 휴가",
|
||||
"description": "그룹 이름을 입력하세요."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "그룹 정보",
|
||||
"placeholder": "그룹 참가자들이 알아야 할 정보는 무엇인가요?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "통화 기호",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "금액 표시할 때 이걸 사용할 거예요."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "기본 통화",
|
||||
"createDescription": "금액, 잔액 전부 이 통화로 표시돼요.",
|
||||
"editDescription": "모든 금액이랑 잔액은 이 통화로 보여요. 통화를 바꿔도 이미 입력한 지출은 바뀌지 않아요. 단, (예: 미국 달러에서 일본 엔으로 바꿀 때처럼) 통화의 소수점 단위가 다를 경우에는 예외가 있을 수 있어요."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "참여자",
|
||||
"description": "참가자 이름을 하나씩 입력해 주세요.",
|
||||
"protectedParticipant": "이 참가자는 지출에 참여 중이라서 지울 수 없어요.",
|
||||
"add": "참가자 추가하기",
|
||||
"John": "민수",
|
||||
"Jane": "수영",
|
||||
"Jack": "길동"
|
||||
},
|
||||
"Settings": {
|
||||
"ActiveUserField": {
|
||||
"description": "사용자가 기본 지출 결제자로 사용돼요.",
|
||||
"placeholder": "참가자를 선택해 주세요",
|
||||
"label": "활성 사용자",
|
||||
"none": "없음"
|
||||
},
|
||||
"save": "저장",
|
||||
"saving": "저장중…",
|
||||
"description": "이 설정들은 기기별로 저장되며, 사용자 경험을 맞춤화하는 데 사용돼요.",
|
||||
"cancel": "취소",
|
||||
"creating": "만드는 중…"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Expense": {
|
||||
"create": "지출 추가하기",
|
||||
"edit": "지출 수정하기",
|
||||
"TitleField": {
|
||||
"label": "지출 이름",
|
||||
"placeholder": "월요일 저녁 식사",
|
||||
"description": "지출 내용을 입력해 주세요."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "지출 날짜",
|
||||
"description": "지출한 날짜를 입력해 주세요."
|
||||
},
|
||||
"currencyField": {
|
||||
"label": "지출 통화",
|
||||
"description": "지출이 결제된 통화예요."
|
||||
},
|
||||
"categoryFieldDescription": "지출 카테고리를 선택해 주세요.",
|
||||
"paidByField": {
|
||||
"label": "결제한 사람",
|
||||
"placeholder": "참가자를 선택해 주세요",
|
||||
"description": "지출을 결제한 사람을 선택해 주세요."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "지출 반복 설정",
|
||||
"description": "지출 반복 주기를 선택해 주세요.",
|
||||
"daily": "매일",
|
||||
"weekly": "매주",
|
||||
"monthly": "매달"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "비용 부담자들",
|
||||
"description": "비용 부담자들을 선택해 주세요."
|
||||
},
|
||||
"splitModeDescription": "지출을 어떻게 나눌지 선택해 주세요."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "금액"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Frankfurter의 환율을 사용해요",
|
||||
"useCustom": "직접 설정한 환율 사용하기",
|
||||
"label": "환율"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "환율 가져오는 중…",
|
||||
"success": "가져온 환율:",
|
||||
"error": "헉, 가장 최근 환율을 가져오지 못했어요.",
|
||||
"staleRate": "적용 중인 환율:",
|
||||
"noRate": "아래에 직접 환율을 입력해 주세요.",
|
||||
"currencyNotFound": "헉, Frankfurter에서 이 날짜 환율을 찾을 수 없어요.",
|
||||
"noDate": "환율을 가져오려면 지출한 날짜를 입력해 주세요.",
|
||||
"dateMismatch": "기준 환율: {date}",
|
||||
"refresh": "다시 불러오기",
|
||||
"customRate": "직접 설정한 환율 사용 중"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "이건 비용 정산이에요"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "카테고리"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "메모"
|
||||
},
|
||||
"SplitModeField": {
|
||||
"label": "분할 방식",
|
||||
"evenly": "똑같이 나누기",
|
||||
"byShares": "균등하지 않게 – 지분별로 나누기",
|
||||
"byPercentage": "균등하지 않게 – 비율로 나누기",
|
||||
"byAmount": "균등하지 않게 – 금액별로 나누기",
|
||||
"saveAsDefault": "기본 분할 방식으로 저장하기"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"description": "정말 이 지출을 삭제할까요? 한 번 삭제하면 되돌릴 수 없어요.",
|
||||
"yes": "네",
|
||||
"cancel": "취소"
|
||||
},
|
||||
"Income": {
|
||||
"TitleField": {
|
||||
"placeholder": "월요일 저녁 식사"
|
||||
}
|
||||
},
|
||||
"reimbursement": "정산",
|
||||
"cancel": "취소"
|
||||
},
|
||||
"Balances": {
|
||||
"title": "잔액",
|
||||
"Reimbursements": {
|
||||
"title": "추천 정산 방법",
|
||||
"description": "참가자 간에 가장 좋은 정산 방안을 알려드려요.",
|
||||
"noImbursements": "그룹에선 따로 정산할 게 없네요 😁",
|
||||
"markAsPaid": "지불 완료로 표시하기",
|
||||
"owes": "<strong>{from}</strong>가 <strong>{to}</strong>에게 돈을 내야 해요"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "현황",
|
||||
"Totals": {
|
||||
"title": "전체 합계",
|
||||
"description": "그룹 전체 지출 요약 입니다.",
|
||||
"groupSpendings": "그룹 총 지출액",
|
||||
"yourSpendings": "나의 총 지출",
|
||||
"yourShare": "나의 총 부담금"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "활동 내역",
|
||||
"description": "이 그룹의 모든 활동 개요예요.",
|
||||
"noActivity": "아직 그룹에 활동 내역이 없어요.",
|
||||
"Groups": {
|
||||
"today": "오늘",
|
||||
"yesterday": "어제",
|
||||
"earlierThisWeek": "이번주 초",
|
||||
"lastWeek": "지난주",
|
||||
"earlierThisMonth": "이번달 초",
|
||||
"lastMonth": "지난달",
|
||||
"earlierThisYear": "올해 초",
|
||||
"lastYear": "작년",
|
||||
"older": "이전"
|
||||
},
|
||||
"settingsModified": "<strong>{participant}</strong>가 그룹 설정을 수정했어요.",
|
||||
"expenseCreated": "<strong>{participant}</strong>가 <em>{expense}</em> 지출 내역을 추가했어요.",
|
||||
"expenseUpdated": "<strong>{participant}</strong>가 <em>{expense}</em> 지출을 수정했어요.",
|
||||
"expenseDeleted": "<strong>{participant}</strong>가 <em>{expense}</em> 지출을 삭제했어요."
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"duplicateParticipantName": "이미 같은 이름을 가진 참가자가 있어요.",
|
||||
"invalidNumber": "잘못된 숫자입니다.",
|
||||
"amountRequired": "금액을 입력해 주세요.",
|
||||
"amountNotZero": "금액은 0이 될 수 없어요.",
|
||||
"amountTenMillion": "금액은 10,000,000보다 작아야 해요.",
|
||||
"ratePositive": "환율은 0보다 커야 해요.",
|
||||
"paidByRequired": "참가자를 선택해 주세요.",
|
||||
"paidForMin1": "",
|
||||
"noZeroShares": "각 몫은 0보다 커야 합니다.",
|
||||
"amountSum": "금액 합계가 지출 금액과 같아야 해요.",
|
||||
"percentageSum": "퍼센트 합이 100이 되어야 합니다.",
|
||||
"min1": "최소 한 글자 이상 입력해 주세요.",
|
||||
"min2": "최소 두 글자 이상 입력해 주세요.",
|
||||
"titleRequired": "제목을 입력해 주세요."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "카테고리 선택...",
|
||||
"noCategory": "카테고리를 찾을 수 없어요.",
|
||||
"Uncategorized": {
|
||||
"heading": "카테고리 없음",
|
||||
"General": "일반"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "엔터테인먼트",
|
||||
"Entertainment": "엔터테인먼트",
|
||||
"Games": "게임",
|
||||
"Movies": "영화",
|
||||
"Music": "음악",
|
||||
"Sports": "스포츠"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "음식 및 음료",
|
||||
"Food and Drink": "음식 및 음료",
|
||||
"Dining Out": "외식",
|
||||
"Groceries": "생필품",
|
||||
"Liquor": "주류"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "집 / 가정",
|
||||
"Home": "집 / 가정",
|
||||
"Electronics": "전자제품",
|
||||
"Furniture": "가구",
|
||||
"Household Supplies": "생활용품",
|
||||
"Maintenance": "수리 / 관리",
|
||||
"Mortgage": "주택 담보 대출",
|
||||
"Pets": "반려동물",
|
||||
"Rent": "임대료 / 월세"
|
||||
},
|
||||
"Life": {
|
||||
"Childcare": "육아 / 보육",
|
||||
"Clothing": "의류",
|
||||
"Donation": "기부",
|
||||
"Education": "교육비",
|
||||
"Gifts": "선물",
|
||||
"Insurance": "보험",
|
||||
"Medical Expenses": "의료비",
|
||||
"Taxes": "세금",
|
||||
"heading": "생활"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "교통비",
|
||||
"Transportation": "교통비",
|
||||
"Bicycle": "자전거",
|
||||
"Bus/Train": "버스/기차",
|
||||
"Car": "자동차",
|
||||
"Gas/Fuel": "주유비",
|
||||
"Hotel": "호텔/숙박",
|
||||
"Parking": "주차비",
|
||||
"Plane": "비행기/항공",
|
||||
"Taxi": "택시"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "공과금",
|
||||
"Utilities": "공과금",
|
||||
"Cleaning": "청소",
|
||||
"Electricity": "전기료",
|
||||
"Heat/Gas": "난방/가스",
|
||||
"Trash": "쓰레기 처리비",
|
||||
"TV/Phone/Internet": "TV/전화/인터넷 요금",
|
||||
"Water": "수도 요금"
|
||||
}
|
||||
},
|
||||
"Homepage": {
|
||||
"button": {
|
||||
"github": "깃허브",
|
||||
"groups": "그룹 보기"
|
||||
},
|
||||
"title": "<strong>친구 & 가족</strong>과 <strong>지출</strong>을 함께 관리해요",
|
||||
"description": "<strong>Spliit</strong>와 함께하는 새 시작을 환영해요!"
|
||||
},
|
||||
"Header": {
|
||||
"groups": "그룹"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "몬트리올, 퀘벡에서 제작 🇨🇦",
|
||||
"builtBy": "<author>Sebastien Castiel</author>와 <source>공동 작업자들</source>이 함께 만들었어요"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "설정"
|
||||
},
|
||||
"Share": {
|
||||
"warningHelp": "그룹 URL을 가진 사람은 누구나 지출 내역을 보고 수정할 수 있어요. 조심해서 공유하세요!",
|
||||
"warning": "경고!",
|
||||
"title": "공유하기",
|
||||
"description": "다른 참가자들이 그룹을 보고 지출을 추가할 수 있도록 URL을 공유해 주세요."
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong>가 <paidFor></paidFor>를 위해 결제함",
|
||||
"everyone": "모두",
|
||||
"yourBalance": "내 잔액:",
|
||||
"notInvolved": "당신은 관련이 없어요"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "파일이 너무 커요",
|
||||
"description": "업로드 가능한 최대 파일 크기: {maxSize}, 현재 파일 크기: {size}입니다."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "문서 업로드 중 오류가 발생했어요",
|
||||
"description": "문서 업로드 중 오류가 발생했습니다. 나중에 다시 시도하거나 다른 파일을 선택해 주세요."
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "영수증으로 지출 추가하기",
|
||||
"description": "영수증 사진으로 지출 내역을 자동으로 불러와요.",
|
||||
"body": "영수증 사진을 업로드하면, 가능하면 지출 정보를 스캔해서 추출해 드려요.",
|
||||
"selectImage": "사진 선택하기…",
|
||||
"titleLabel": "제목:",
|
||||
"categoryLabel": "카테고리:",
|
||||
"amountLabel": "금액:",
|
||||
"dateLabel": "날짜:",
|
||||
"editNext": "다음 단계에서 지출 정보를 수정할 수 있습니다.",
|
||||
"continue": "계속하기"
|
||||
},
|
||||
"TooBigToast": {
|
||||
"title": "파일이 너무 커요",
|
||||
"description": "업로드 가능한 최대 파일 크기: {maxSize}, 현재 파일 크기: {size}입니다."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "문서 업로드 중 오류가 발생했어요",
|
||||
"description": "문서 업로드 중 오류가 발생했습니다. 나중에 다시 시도하거나 다른 파일을 선택해 주세요."
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "정보",
|
||||
"empty": "아직 그룹 정보가 없어요."
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "통화 선택하기...",
|
||||
"noCurrency": "통화를 찾을 수 없어요.",
|
||||
"common": {
|
||||
"heading": "가장 많이 쓰이는"
|
||||
},
|
||||
"other": {
|
||||
"heading": "그 외 통화"
|
||||
}
|
||||
}
|
||||
}
|
||||
459
messages/nl-NL.json
Normal file
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Deel <strong>Uitgaven</strong> met <strong>Vrienden & Familie</strong>",
|
||||
"description": "Welkom op je nieuwe <strong>Spliit</strong>-instantie!",
|
||||
"button": {
|
||||
"groups": "Ga naar groepen",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Groepen"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Gemaakt in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Geschreven door <author>Sebastien Castiel</author> en <source>bijdragers</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Uitgaven",
|
||||
"description": "Dit zijn de uitgaven die je gemaakt hebt voor je groep.",
|
||||
"create": "Maak uitgave",
|
||||
"createFirst": "Maak de eerste",
|
||||
"noExpenses": "Je groep heeft nog geen uitgaven.",
|
||||
"export": "Exporteren",
|
||||
"exportJson": "Exporteren naar JSON",
|
||||
"exportCsv": "Exporteren naar CSV",
|
||||
"searchPlaceholder": "Zoek naar een uitgave…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Wie ben jij?",
|
||||
"description": "Zeg ons welke deelnemer je bent zodat wij persoonlijke informatie kunnen aantonen.",
|
||||
"nobody": "Ik wil niemand selecteren",
|
||||
"save": "Opslaan",
|
||||
"footer": "Deze instelling kan later worden gewijzigd in de instellingen van de groep."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Aankomend",
|
||||
"thisWeek": "Deze week",
|
||||
"earlierThisMonth": "Eerder deze maand",
|
||||
"lastMonth": "Vorige maand",
|
||||
"earlierThisYear": "Eerder dit jaar",
|
||||
"lastYear": "Vorig jaar",
|
||||
"older": "Ouder"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Betaald door <strong>{paidBy}</strong> voor <paidFor></paidFor>",
|
||||
"everyone": "iedereen",
|
||||
"receivedBy": "Ontvangen door <strong>{paidBy}</strong> voor <paidFor></paidFor>",
|
||||
"yourBalance": "Jouw balans:",
|
||||
"notInvolved": "Je bent hier niet bij betrokken"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Mijn groepen",
|
||||
"create": "Maak",
|
||||
"loadingRecent": "Recente groepen laden…",
|
||||
"NoRecent": {
|
||||
"description": "Je hebt de laatste tijd geen groepen bezocht.",
|
||||
"create": "Maak er één",
|
||||
"orAsk": "of vraag een vriend om je de link naar een bestaande groep te sturen."
|
||||
},
|
||||
"recent": "Recente groepen",
|
||||
"starred": "Favoriete groepen",
|
||||
"archived": "Gearchiveerde groepen",
|
||||
"archive": "Archiveer groep",
|
||||
"unarchive": "Herstel groep",
|
||||
"removeRecent": "Verwijder uit recente groepen",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Groep verwijderd",
|
||||
"description": "Deze groep is verwijderd uit je recente groepen.",
|
||||
"undoAlt": "Maak het verwijderen van de groep ongedaan",
|
||||
"undo": "Ongedaan maken"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Voeg toe met URL",
|
||||
"title": "Voeg een groep toe met een URL",
|
||||
"description": "Als een groep met je gedeeld is, kun je de URL hier plakken om deze aan je lijst toe te voegen.",
|
||||
"error": "Oeps, we kunnen de groep niet vinden met de URL die je hebt opgegeven…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Deze groep bestaat niet.",
|
||||
"link": "Ga naar je recente groepen"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Groepsinformatie",
|
||||
"NameField": {
|
||||
"label": "Groepsnaam",
|
||||
"placeholder": "Zomervakantie",
|
||||
"description": "Geef je groep een naam."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Groepsinformatie",
|
||||
"placeholder": "Welke informatie is relevant voor de groep?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Symbool van de valuta",
|
||||
"placeholder": "€, $, £…",
|
||||
"description": "Die gebruiken we om de bedragen in de groep aan te geven."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Deelnemers",
|
||||
"description": "Voer de naam in van de deelnemers in de groep.",
|
||||
"protectedParticipant": "Deze deelnemer maakt deel uit van de uitgaven en kan niet worden verwijderd.",
|
||||
"new": "Nieuwe deelnemer",
|
||||
"add": "Voeg deelnemer toe",
|
||||
"John": "Jan",
|
||||
"Jane": "Julia",
|
||||
"Jack": "Jacob"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Lokale instellingen",
|
||||
"description": "Deze instellingen worden per apparaat ingesteld en worden gebruikt om je ervaring aan te passen.",
|
||||
"ActiveUserField": {
|
||||
"label": "Huidige gebruiker",
|
||||
"placeholder": "Selecteer een deelnemer",
|
||||
"none": "Geen",
|
||||
"description": "De deelnemer die automatisch wordt geselecteerd als je een uitgave maakt."
|
||||
},
|
||||
"save": "Opslaan",
|
||||
"saving": "Aan het opslaan…",
|
||||
"create": "Groep maken",
|
||||
"creating": "Aan het maken…",
|
||||
"cancel": "Annuleren"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Hoofdvaluta",
|
||||
"createDescription": "Alle hoeveelheden en saldi worden in deze valuta weergegeven.",
|
||||
"editDescription": "Alle bedragen en saldi worden in deze valuta weergegeven. Als je dit wijzigt, worden reeds ingevoerde uitgaven NIET omgerekend, behalve wanneer de valuta andere \"kleinste eenheden\" heeft dan de huidige (bijvoorbeeld bij een wijziging van Amerikaanse dollar naar Japanse yen)",
|
||||
"customOption": "Aangepast"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Maak inkomen",
|
||||
"edit": "Bewerk inkomen",
|
||||
"TitleField": {
|
||||
"label": "Titel inkomen",
|
||||
"placeholder": "Restaurant maandagavond",
|
||||
"description": "Voer een beschrijving in voor het inkomen."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum inkomen",
|
||||
"description": "Voer de datum in waarop het inkomen is ontvangen."
|
||||
},
|
||||
"categoryFieldDescription": "Selecteer de inkomencategorie.",
|
||||
"paidByField": {
|
||||
"label": "Ontvangen door",
|
||||
"description": "Selecteer de deelnemer die het inkomen heeft ontvangen."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Terugkerend inkomen",
|
||||
"description": "Kies hoe vaak het inkomen herhaald wordt.",
|
||||
"none": "Niet",
|
||||
"daily": "Dagelijks",
|
||||
"weekly": "Wekelijks",
|
||||
"monthly": "Maandelijks"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Ontvangen voor",
|
||||
"description": "Selecteer voor wie het inkomen is ontvangen."
|
||||
},
|
||||
"splitModeDescription": "Selecteer hoe het inkomen verdeeld moet worden.",
|
||||
"attachDescription": "Bekijk en voeg bijlagen toe aan het inkomen.",
|
||||
"currencyField": {
|
||||
"label": "Munteenheid van inkomen",
|
||||
"description": "De munteenheid waar het inkomen in is ontvangen."
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Maak uitgave",
|
||||
"edit": "Bewerk uitgave",
|
||||
"TitleField": {
|
||||
"label": "Titel uitgave",
|
||||
"placeholder": "Restaurant maandagavond",
|
||||
"description": "Voer een beschrijving in voor de uitgave."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Datum uitgave",
|
||||
"description": "Voer de datum in waarop de uitgave is gedaan."
|
||||
},
|
||||
"categoryFieldDescription": "Selecteer de uitgavecategorie.",
|
||||
"paidByField": {
|
||||
"label": "Betaald door",
|
||||
"description": "Selecteer de deelnemer die de uitgave heeft gedaan.",
|
||||
"placeholder": "Selecteer een deelnemer"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Terugkerende uitgave",
|
||||
"description": "Kies hoe vaak de uitgave herhaald wordt.",
|
||||
"none": "Niet",
|
||||
"daily": "Dagelijks",
|
||||
"weekly": "Wekelijks",
|
||||
"monthly": "Maandelijks"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Betaald voor",
|
||||
"description": "Selecteer voor wie de uitgave is gedaan."
|
||||
},
|
||||
"splitModeDescription": "Selecteer hoe de uitgave verdeeld moet worden.",
|
||||
"attachDescription": "Bekijk en voeg bijlagen toe aan de uitgave.",
|
||||
"currencyField": {
|
||||
"label": "Munteenheid van uitgave",
|
||||
"description": "De munteenheid waar de uitgave in is betaald."
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Bedrag"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Dit is een terugbetaling"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categorie"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notities"
|
||||
},
|
||||
"selectNone": "Selecteer niemand",
|
||||
"selectAll": "Selecteer iedereen",
|
||||
"shares": "deel/delen",
|
||||
"advancedOptions": "Andere split-opties…",
|
||||
"SplitModeField": {
|
||||
"label": "Split soort",
|
||||
"evenly": "Gelijk verdeeld",
|
||||
"byShares": "Ongelijk – Met delen",
|
||||
"byPercentage": "Ongelijk – Met percentage",
|
||||
"byAmount": "Ongelijk – Met bedrag",
|
||||
"saveAsDefault": "Opslaan als standaard-optie"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Verwijderen",
|
||||
"title": "Deze uitgave verwijderen?",
|
||||
"description": "Wil je deze uitgave echt verwijderen? Dit kan niet ongedaan worden.",
|
||||
"yes": "Ja",
|
||||
"cancel": "Annuleer"
|
||||
},
|
||||
"attachDocuments": "Voeg documenten toe",
|
||||
"create": "Maken",
|
||||
"creating": "Aan het maken…",
|
||||
"save": "Opslaan",
|
||||
"saving": "Aan het opslaan…",
|
||||
"cancel": "Annuleren",
|
||||
"reimbursement": "Terugbetaling",
|
||||
"conversionUnavailable": "Om een andere munteenheid in te stellen voor een uitgave en bedragen om te rekenen, kies een standaard munteenheid voor de groep.",
|
||||
"originalAmountField": {
|
||||
"label": "Om te rekenen bedrag"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "Gebruik koersen van Frankfurter",
|
||||
"useCustom": "Gebruik aangepaste koers",
|
||||
"label": "Wisselkoers"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"loading": "Wisselkoers aan het ophalen…",
|
||||
"success": "Verkregen wisselkoers:",
|
||||
"error": "Oeps, we konden de nieuwste wisselkoersen niet verkrijgen.",
|
||||
"staleRate": "Gebruikte wisselkoers:",
|
||||
"noRate": "Voer hieronder een aangepaste koers in.",
|
||||
"currencyNotFound": "Oeps, Frankfurter heeft geen koersen voor deze munteenheid op deze dag.",
|
||||
"noDate": "Voer de datum van de uitgave in om een wisselkoers te krijgen.",
|
||||
"dateMismatch": "Wisselkoers van: {date}",
|
||||
"refresh": "Ververs",
|
||||
"customRate": "Aangepaste wisselkoers wordt gebruikt"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Het bestand is te groot",
|
||||
"description": "De maximum bestandsgrootte {maxSize}. Jouw bestand is {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Fout bij het uploaden van document",
|
||||
"description": "Er is iets mis gegaan bij het uploaden van het document. Probeer het later opnieuw of kies een ander bestand.",
|
||||
"retry": "Probeer opnieuw"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Uitgave maken van foto",
|
||||
"title": "Maak uitgave van foto",
|
||||
"description": "Uitgave-informatie van een foto van een bon lezen.",
|
||||
"body": "Upload de foto van een bon, en we lezen de uitgave-informatie eruit.",
|
||||
"selectImage": "Selecteer foto…",
|
||||
"titleLabel": "Titel:",
|
||||
"categoryLabel": "Categorie:",
|
||||
"amountLabel": "Bedrag:",
|
||||
"dateLabel": "Datum:",
|
||||
"editNext": "Hierna kun je de uitgave-informatie bewerken.",
|
||||
"continue": "Doorgaan"
|
||||
},
|
||||
"unknown": "Onbekend",
|
||||
"TooBigToast": {
|
||||
"title": "Het bestand is te groot",
|
||||
"description": "De maximum bestandsgrootte {maxSize}. Jouw bestand is {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Fout bij het uploaden van document",
|
||||
"description": "Er is iets mis gegaan bij het uploaden van het document. Probeer het later opnieuw of kies een ander bestand.",
|
||||
"retry": "Probeer opnieuw"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Balans",
|
||||
"description": "Dit zijn de bedragen die elke deelnemer heeft betaald of waarvoor is betaald.",
|
||||
"Reimbursements": {
|
||||
"title": "Voorgestelde terugbetalingen",
|
||||
"description": "Dit zijn de voorgestelde terugbetalingen tussen deelnemers.",
|
||||
"noImbursements": "Lijkt erop dat je groep geen terugbetalingen nodig heeft 😁",
|
||||
"owes": "<strong>{from}</strong> betaalt aan <strong>{to}</strong>",
|
||||
"markAsPaid": "Markeer als betaald"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistieken",
|
||||
"Totals": {
|
||||
"title": "Totaaluitgaven",
|
||||
"description": "Uitgavenoverzicht van de hele groep.",
|
||||
"groupSpendings": "Totale uitgaven van de groep",
|
||||
"groupEarnings": "Totale inkomsten van de groep",
|
||||
"yourSpendings": "Jouw totale uitgaven",
|
||||
"yourEarnings": "Jouw totale inkomsten",
|
||||
"yourShare": "Jouw totale aandeel"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Gebeurtenissen",
|
||||
"description": "Overzicht van de gebeurtenissen in je groep.",
|
||||
"noActivity": "Er zijn geen gebeurtenissen in deze groep.",
|
||||
"someone": "Iemand",
|
||||
"settingsModified": "Groepsinstellingen zijn aangepast door <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Uitgave <em>{expense}</em> gemaakt door <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Uitgave <em>{expense}</em> bewerkt door <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Uitgave <em>{expense}</em> verwijderd door <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Vandaag",
|
||||
"yesterday": "Gisteren",
|
||||
"earlierThisWeek": "Eerder deze week",
|
||||
"lastWeek": "Vorige week",
|
||||
"earlierThisMonth": "Eerder deze maand",
|
||||
"lastMonth": "Vorige maand",
|
||||
"earlierThisYear": "Eerder dit jaar",
|
||||
"lastYear": "Vorig jaar",
|
||||
"older": "Ouder"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informatie",
|
||||
"description": "Gebruike deze plek om informatie toe te voegen die relevant kan zijn voor de groepsleden.",
|
||||
"empty": "Nog geen informatie toegevoegd."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Instellingen"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Delen",
|
||||
"description": "Om andere deelnemers de groep te laten zien en uitgaven toe te voegen, deel je de URL met hen.",
|
||||
"warning": "Waarschuwing!",
|
||||
"warningHelp": "Iedereen met de groeps-URL kan de uitgaven zien en bewerken. Deel voorzichtig!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Vul ten minste één karakter in.",
|
||||
"min2": "Vul ten minste twee karakters in.",
|
||||
"max5": "Vul maximaal vijf karakters in.",
|
||||
"max50": "Vul maximaal 50 karakters in.",
|
||||
"duplicateParticipantName": "Er is al een deelnemer met deze naam.",
|
||||
"titleRequired": "Vul een titel in.",
|
||||
"invalidNumber": "Ongeldig getal.",
|
||||
"amountRequired": "Vul een bedrag in.",
|
||||
"amountNotZero": "Het bedrag moet hoger zijn dan 0.",
|
||||
"amountTenMillion": "Het bedrag mag niet hoger zijn dan 10,000,000.",
|
||||
"paidByRequired": "Selecteer een deelnemer die de uitgave heeft gedaan.",
|
||||
"paidForMin1": "De uitgave moet voor ten minste één deelnemer zijn gedaan.",
|
||||
"noZeroShares": "Een deel mag niet 0 zijn.",
|
||||
"amountSum": "Het totaalbedrag moet gelijk zijn aan het uitgavebedrag.",
|
||||
"percentageSum": "Het totaalpercentage moet gelijk zijn aan 100%.",
|
||||
"ratePositive": "De koers moet groter dan nul zijn."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Categorie zoeken…",
|
||||
"noCategory": "Geen categorieën gevonden.",
|
||||
"Uncategorized": {
|
||||
"heading": "Geen categorie",
|
||||
"General": "Algemeen",
|
||||
"Payment": "Betaling"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Vermaak",
|
||||
"Entertainment": "Vermaak",
|
||||
"Games": "Games",
|
||||
"Movies": "Film",
|
||||
"Music": "Muziek",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Eten en Drinken",
|
||||
"Food and Drink": "Eten en Drinken",
|
||||
"Dining Out": "Uit eten",
|
||||
"Groceries": "Boodschappen",
|
||||
"Liquor": "Drank"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Thuis",
|
||||
"Home": "Thuis",
|
||||
"Electronics": "Elektronica",
|
||||
"Furniture": "Meubels",
|
||||
"Household Supplies": "Huishoudelijke artikelen",
|
||||
"Maintenance": "Onderhoud",
|
||||
"Mortgage": "Hypotheek",
|
||||
"Pets": "Huisdieren",
|
||||
"Rent": "Huur",
|
||||
"Services": "Diensten"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Leven",
|
||||
"Childcare": "Kinderopvang",
|
||||
"Clothing": "Kleding",
|
||||
"Donation": "Donatie",
|
||||
"Education": "Onderwijs",
|
||||
"Gifts": "Cadeaus",
|
||||
"Insurance": "Verzekering",
|
||||
"Medical Expenses": "Medische kosten",
|
||||
"Taxes": "Belastingen"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Vervoer",
|
||||
"Transportation": "Vervoer",
|
||||
"Bicycle": "Fiets",
|
||||
"Bus/Train": "Bus/Trein",
|
||||
"Car": "Auto",
|
||||
"Gas/Fuel": "Tanken",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parkeren",
|
||||
"Plane": "Vliegtuig",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Nutsvoorzieningen",
|
||||
"Utilities": "Nutsvoorzieningen",
|
||||
"Cleaning": "Schoonmaak",
|
||||
"Electricity": "Elektriciteit",
|
||||
"Heat/Gas": "Verwarming/Gas",
|
||||
"Trash": "Afval",
|
||||
"TV/Phone/Internet": "Internet/TV/Telefoon",
|
||||
"Water": "Water"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"noCurrency": "Geen valuta gevonden.",
|
||||
"custom": {
|
||||
"heading": "Aangepast"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Meest voorkomend"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Andere valuta"
|
||||
},
|
||||
"search": "Valuta zoeken..."
|
||||
}
|
||||
}
|
||||
399
messages/pl-PL.json
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Dziel <strong>Wydatki</strong> z <strong>Rodziną i Przyjaciółmi</strong>",
|
||||
"description": "Witaj na Twojej nowej instancji <strong>Spliit</strong> !",
|
||||
"button": {
|
||||
"groups": "Przejdź do grup",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grupy"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Stworzone w Montréalu, Québec 🇨🇦",
|
||||
"builtBy": "Napisane przez <author>Sebastien Castiela</author> i <source>kontrybutorów</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Wydatki",
|
||||
"description": "Tutaj są wydatki, które utworzyłeś dla Twojej grupy.",
|
||||
"create": "Dodaj wydatek",
|
||||
"createFirst": "Stwórz swój pierwszy",
|
||||
"noExpenses": "Twoja grupa nie ma jeszcze żadnych wydatków.",
|
||||
"export": "Eksportuj",
|
||||
"exportJson": "Eksportuj jako JSON",
|
||||
"exportCsv": "Eksportuj jako CSV",
|
||||
"searchPlaceholder": "Szukaj wydatku…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Kim jesteś?",
|
||||
"description": "Podaj, którym uczestnikiem jesteś aby pozwolić nam określić jakie informacje mają być wyświetlane.",
|
||||
"nobody": "Nie chcę wybierać nikogo",
|
||||
"save": "Zapisz zmiany",
|
||||
"footer": "To ustawienie może być potem zmienione w ustawieniach grupy."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Nadchodzące",
|
||||
"thisWeek": "Ten tydzień",
|
||||
"earlierThisMonth": "Wcześniej w tym miesiącu",
|
||||
"lastMonth": "Ostatni miesiąc",
|
||||
"earlierThisYear": "Wcześniej w tym roku",
|
||||
"lastYear": "Poprzedni rok",
|
||||
"older": "Starsze"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Opłacone przez <strong>{paidBy}</strong> dla <paidFor></paidFor>",
|
||||
"receivedBy": "Otrzymane przez <strong>{paidBy}</strong> od <paidFor></paidFor>",
|
||||
"yourBalance": "Twjoje saldo:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Moje grupy",
|
||||
"create": "Stwórz",
|
||||
"loadingRecent": "Wczytywanie ostatnich grup…",
|
||||
"NoRecent": {
|
||||
"description": "Nie odwiedzałeś ostatnio żadnych grup.",
|
||||
"create": "Stwórz",
|
||||
"orAsk": "albo poproś przyjaciela, aby wysłał Ci link do już istniejącej."
|
||||
},
|
||||
"recent": "Ostatnie grupy",
|
||||
"starred": "Ulubione grupy",
|
||||
"archived": "Zarchiwizowane grupy",
|
||||
"archive": "Zarchiwizuj grupę",
|
||||
"unarchive": "Cofnij archiwizację grupy",
|
||||
"removeRecent": "Usuń z ostatnich grup",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Grupa została usunięta",
|
||||
"description": "Grupa została usunięta z listy twoich ostatnich grup.",
|
||||
"undoAlt": "Cofnij usunięcie grupy",
|
||||
"undo": "Cofnij"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Dodaj poprzez adres URL",
|
||||
"title": "Dodaj grupę poprzez adres URL",
|
||||
"description": "Jeśli grupa została Ci udostępniona, możesz wkleić jej adres URL tutaj, aby dodać ją do Twojej listy.",
|
||||
"error": "Ups, nie możemy znaleźć grupy o podanym adresie URL…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Ta grupa nie istnieje.",
|
||||
"link": "Idź do ostatnio odwiedzanych grup"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informacje o grupie",
|
||||
"NameField": {
|
||||
"label": "Nazwa grupy",
|
||||
"placeholder": "Letni wyjazd",
|
||||
"description": "Podaj nazwę dla grupy."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informacje o grupie",
|
||||
"placeholder": "Jakie informacje mogą być ważne dla członków grupy?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Symbol waluty",
|
||||
"placeholder": "PLN, zł, $, €, £…",
|
||||
"description": "Użyjemy go do wyświetlania kwot."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Członkowie",
|
||||
"description": "Podaj nazwę dla każdego członka.",
|
||||
"protectedParticipant": "Ten członek wciąż bierze udział w rozliczeniach i nie może być usunięty.",
|
||||
"new": "Nowy",
|
||||
"add": "Dodaj członka",
|
||||
"John": "Jan",
|
||||
"Jane": "Joanna",
|
||||
"Jack": "Jacek"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ustawienia lokalne",
|
||||
"description": "Te ustawienia są zapisywane na tym urządzeniu i służą do dostosowania Twoich doświadczeń z aplikacją.",
|
||||
"ActiveUserField": {
|
||||
"label": "Aktywny użytkownik",
|
||||
"placeholder": "Wybierz użytkownika",
|
||||
"none": "Brak",
|
||||
"description": "Użytkownik używany domyślnie do wprowadzania wydatków."
|
||||
},
|
||||
"save": "Zapisz",
|
||||
"saving": "Zapisywanie…",
|
||||
"create": "Stwórz",
|
||||
"creating": "Tworzenie…",
|
||||
"cancel": "Anuluj"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Dodaj wpływ",
|
||||
"edit": "Edytuj wpływ",
|
||||
"TitleField": {
|
||||
"label": "Tytuł wpływu",
|
||||
"placeholder": "Zwrot kaucji",
|
||||
"description": "Podaj opis wpływu."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data wpływu",
|
||||
"description": "Podaj datę otrzymania wpływu."
|
||||
},
|
||||
"categoryFieldDescription": "Wybierz kategorię wpływu.",
|
||||
"paidByField": {
|
||||
"label": "Otrzymane przez",
|
||||
"description": "Wybierz członka, który otrzymał wpływ."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Otrzymany dla",
|
||||
"description": "Podaj dla kogo wpływ był przeznaczony."
|
||||
},
|
||||
"splitModeDescription": "Wybierz jak podzielić wpływ.",
|
||||
"attachDescription": "Zobacz i załącz rachunki do wpływu."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Stwórz wydatek",
|
||||
"edit": "Edytuj wydatek",
|
||||
"TitleField": {
|
||||
"label": "Tytuł wydatku",
|
||||
"placeholder": "Poniedziałkowe wyjście do restauracji",
|
||||
"description": "Podaj opis wydatku."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data wydatku",
|
||||
"description": "Podaj datę wydatku."
|
||||
},
|
||||
"categoryFieldDescription": "Podaj kategorię wydatku.",
|
||||
"paidByField": {
|
||||
"label": "Opłacone przez",
|
||||
"description": "Wybierz członka, który zapłacił."
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "Powtarzalnośc wydatku",
|
||||
"description": "Wybierz jak często wydatek ma się powtarzać.",
|
||||
"none": "Jednorazowo",
|
||||
"daily": "Codziennie",
|
||||
"weekly": "Co tydzień",
|
||||
"monthly": "Co miesiąc"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Opłacone dla",
|
||||
"description": "Wybierz kogo dotyczył wydatek."
|
||||
},
|
||||
"splitModeDescription": "Wybierz jak podzielić wydatek.",
|
||||
"attachDescription": "Zobacz i załącz rachunki do wydatku."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Suma"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Oznacz jako zwrot kosztów"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notatki"
|
||||
},
|
||||
"selectNone": "Nie wybieraj nikogo",
|
||||
"selectAll": "Wybierz wszystkich",
|
||||
"shares": "udział(y)",
|
||||
"advancedOptions": "Zaawansowane opcje podziału…",
|
||||
"SplitModeField": {
|
||||
"label": "Typ podziału",
|
||||
"evenly": "Równy",
|
||||
"byShares": "Nierówny – Poprzez udziały",
|
||||
"byPercentage": "Nierówny – Procentowo",
|
||||
"byAmount": "Nierówny – Na konkretne sumy",
|
||||
"saveAsDefault": "Wybierz jako domyślny typ podziału"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Usuń",
|
||||
"title": "Usunąć ten wydatek?",
|
||||
"description": "Czy na pewno chcesz usunąć ten wydatek? Ta akcja jest nieodwracalna.",
|
||||
"yes": "Tak",
|
||||
"cancel": "Anuluj"
|
||||
},
|
||||
"attachDocuments": "Załącz dokumenty",
|
||||
"create": "Stwórz",
|
||||
"creating": "Tworzenie…",
|
||||
"save": "Zapisz",
|
||||
"saving": "Zapisywanie…",
|
||||
"cancel": "Anuluj",
|
||||
"reimbursement": "Zwrot środków"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Ten plik jest zbyt duży",
|
||||
"description": "Maksymalny rozmiar pliku to {maxSize}. Twój plik ma {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Błąd podczas wysyłania dokumentu",
|
||||
"description": "Coś poszło nie tak podczas wysyłania dokumentu. Proszę spróbuj ponownie później, albo wybierz inny plik.",
|
||||
"retry": "Ponów"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Utwórz wydatek z paragonu",
|
||||
"title": "Utwórz z paragonu",
|
||||
"description": "Wyodrębnij informacje o wydatkach ze zdjęcia paragonu.",
|
||||
"body": "Prześlij zdjęcie paragonu, a my zeskanujemy je, aby wyodrębnić informacje o wydatkach, jeśli to możliwe.",
|
||||
"selectImage": "Wybierz obraz…",
|
||||
"titleLabel": "Tytuł:",
|
||||
"categoryLabel": "Kategoria:",
|
||||
"amountLabel": "Suma:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "Następnie będziesz mógł edytować informacje o wydatkach.",
|
||||
"continue": "Kontynuuj"
|
||||
},
|
||||
"unknown": "Nieznany",
|
||||
"TooBigToast": {
|
||||
"title": "Ten plik jest zbyt duży",
|
||||
"description": "Maksymalny rozmiar pliku to {maxSize}. Twój plik ma {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Błąd podczas wysyłania dokumentu",
|
||||
"description": "Coś poszło nie tak podczas wysyłania dokumentu. Proszę spróbuj ponownie później, albo wybierz inny plik.",
|
||||
"retry": "Ponów"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Salda",
|
||||
"description": "Jest to kwota, którą każdy członek zapłacił lub otrzymał.",
|
||||
"Reimbursements": {
|
||||
"title": "Sugerowane zwroty",
|
||||
"description": "Oto sugestie dotyczące optymalizacji zwrotów między uczestnikami.",
|
||||
"noImbursements": "Wygląda na to, że w twojej grupie nie ma potrzeby żadnych zwrotów 😁",
|
||||
"owes": "<strong>{from}</strong> jest winny dla <strong>{to}</strong>",
|
||||
"markAsPaid": "Zaznacz jako opłacone"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statystyki",
|
||||
"Totals": {
|
||||
"title": "Podsumowanie",
|
||||
"description": "Podsumowanie wydatków dla całej grupy.",
|
||||
"groupSpendings": "Wydatki grupy",
|
||||
"groupEarnings": "Wpływy grupy",
|
||||
"yourSpendings": "Twoje wydatki",
|
||||
"yourEarnings": "Twoje wpływy",
|
||||
"yourShare": "Twoje udziały"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Aktywność",
|
||||
"description": "Przegląd wszystkich działań w tej grupie.",
|
||||
"noActivity": "W grupie nie ma jeszcze żadnej aktywności.",
|
||||
"someone": "Ktoś",
|
||||
"settingsModified": "Ustawienia grupy zostały zmienione przez <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Wydatek <em>{expense}</em> stworzony przez <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Wydatek <em>{expense}</em> zaktualizowany przez <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Wydatek <em>{expense}</em> usunięty przez <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Dzisiaj",
|
||||
"yesterday": "Wczoraj",
|
||||
"earlierThisWeek": "Wcześniej w tym tygodniu",
|
||||
"lastWeek": "W zeszłym tygodniu",
|
||||
"earlierThisMonth": "Wcześniej w tym miesiącu",
|
||||
"lastMonth": "Ostatni miesiąc",
|
||||
"earlierThisYear": "Wcześniej w tym roku",
|
||||
"lastYear": "Poprzedni rok",
|
||||
"older": "Starsze"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informacje",
|
||||
"description": "Użyj tego miejsca, aby dodać wszelkie informacje, które mogą być istotne dla uczestników grupy.",
|
||||
"empty": "Jeszcze nic tu nie ma."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ustawienia"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Udostępnij",
|
||||
"description": "Aby inni uczestnicy mogli zobaczyć grupę i dodać wydatki, udostępnij im jej adres URL.",
|
||||
"warning": "Uwaga!",
|
||||
"warningHelp": "Każda osoba posiadająca adres URL grupy będzie mogła przeglądać i edytować wydatki. Udostępniaj ostrożnie!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Wprowadź co najmniej jeden znak.",
|
||||
"min2": "Wprowadź co najmniej dwa znaki.",
|
||||
"max5": "Wprowadź maksymalnie pięć znaków.",
|
||||
"max50": "Wprowadź maksymalnie 50 znaków.",
|
||||
"duplicateParticipantName": "Ta nazwa jest już zajęta.",
|
||||
"titleRequired": "Podaj tytuł.",
|
||||
"invalidNumber": "Niewłaściwa liczba.",
|
||||
"amountRequired": "Należy wprowadzić kwotę.",
|
||||
"amountNotZero": "Kwota nie może być zerem.",
|
||||
"amountTenMillion": "Kwota musi być niższa niż 10 000 000.",
|
||||
"paidByRequired": "Musisz wybrać członka.",
|
||||
"paidForMin1": "Wydatek musi zostać opłacony za co najmniej jednego uczestnika.",
|
||||
"noZeroShares": "Wszystkie udziały muszą być większe niż 0.",
|
||||
"amountSum": "Suma udziałów musi być równa wydatkowi.",
|
||||
"percentageSum": "Suma procentów musi być równa 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Szukaj kategorii…",
|
||||
"noCategory": "Nie znaleziono kategorii.",
|
||||
"Uncategorized": {
|
||||
"heading": "Bez kategorii",
|
||||
"General": "Ogólne",
|
||||
"Payment": "Płatność"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Rozrywka",
|
||||
"Entertainment": "Rozrywka",
|
||||
"Games": "Gry",
|
||||
"Movies": "Filmy",
|
||||
"Music": "Muzyka",
|
||||
"Sports": "Sport"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Jedzenie i Napoje",
|
||||
"Food and Drink": "Jedzenie i Napoje",
|
||||
"Dining Out": "Jedzenie na mieście",
|
||||
"Groceries": "Zakupy",
|
||||
"Liquor": "Alkohole"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Dom",
|
||||
"Home": "Dom",
|
||||
"Electronics": "Elektronika",
|
||||
"Furniture": "Meble",
|
||||
"Household Supplies": "Artykuły gospodarstwa domowego",
|
||||
"Maintenance": "Utrzymanie",
|
||||
"Mortgage": "Kredyt",
|
||||
"Pets": "Zwierzaki",
|
||||
"Rent": "Czynsz",
|
||||
"Services": "Usługi"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Życie",
|
||||
"Childcare": "Opieka nad dzieckiem",
|
||||
"Clothing": "Ubrania",
|
||||
"Donation": "Darowizna",
|
||||
"Education": "Edukacja",
|
||||
"Gifts": "Prezenty",
|
||||
"Insurance": "Ubezpieczenie",
|
||||
"Medical Expenses": "Wydatki medyczne",
|
||||
"Taxes": "Podatki"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transport",
|
||||
"Transportation": "Transport",
|
||||
"Bicycle": "Rower",
|
||||
"Bus/Train": "Autobus/Pociąg",
|
||||
"Car": "Samochód",
|
||||
"Gas/Fuel": "Paliwo",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parking",
|
||||
"Plane": "Samolot",
|
||||
"Taxi": "Taksówka"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Media",
|
||||
"Utilities": "Media",
|
||||
"Cleaning": "Sprzątanie",
|
||||
"Electricity": "Prąd",
|
||||
"Heat/Gas": "Ogrzewanie/Gaz",
|
||||
"Trash": "Śmieci",
|
||||
"TV/Phone/Internet": "TV/Telefon/Internet",
|
||||
"Water": "Woda"
|
||||
}
|
||||
}
|
||||
}
|
||||
429
messages/pt-BR.json
Normal file
@@ -0,0 +1,429 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Compartilhe <strong>Despesas</strong> com <strong>Amigos e Família</strong>",
|
||||
"description": "Bem-vindo à sua nova instalação do <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Ir para grupos",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grupos"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Feito em Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Desenvolvido por <author>Sebastien Castiel</author> e <source>contribuidores</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Despesas",
|
||||
"description": "Aqui estão as despesas que você criou para o seu grupo.",
|
||||
"create": "Criar despesa",
|
||||
"createFirst": "Crie a primeira",
|
||||
"noExpenses": "Seu grupo ainda não contém nenhuma despesa.",
|
||||
"exportJson": "Exportar para JSON",
|
||||
"exportCsv": "Exportar para CSV",
|
||||
"searchPlaceholder": "Pesquisar por uma despesa…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Quem é você?",
|
||||
"description": "Informe qual participante você é para personalizarmos a exibição das informações.",
|
||||
"nobody": "Não quero selecionar ninguém",
|
||||
"save": "Salvar alterações",
|
||||
"footer": "Essa configuração pode ser alterada posteriormente nas configurações do grupo."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Próximas",
|
||||
"thisWeek": "Esta semana",
|
||||
"earlierThisMonth": "Anteriores neste mês",
|
||||
"lastMonth": "Mês passado",
|
||||
"earlierThisYear": "Anteriores neste ano",
|
||||
"lastYear": "Ano passado",
|
||||
"older": "Mais antigas"
|
||||
},
|
||||
"export": "Exportar"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Pago por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"receivedBy": "Recebido por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"yourBalance": "Seu saldo:",
|
||||
"everyone": "Todos",
|
||||
"notInvolved": "Você não está envolvido"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Meus grupos",
|
||||
"create": "Criar",
|
||||
"loadingRecent": "Carregando grupos recentes…",
|
||||
"NoRecent": {
|
||||
"description": "Você não visitou nenhum grupo recentemente.",
|
||||
"create": "Crie um",
|
||||
"orAsk": "ou peça a um amigo para enviar o link de um existente."
|
||||
},
|
||||
"recent": "Grupos recentes",
|
||||
"starred": "Grupos favoritos",
|
||||
"archived": "Grupos arquivados",
|
||||
"archive": "Arquivar grupo",
|
||||
"unarchive": "Desarquivar grupo",
|
||||
"removeRecent": "Remover dos grupos recentes",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Grupo removido",
|
||||
"description": "O grupo foi removido da sua lista de grupos recentes.",
|
||||
"undoAlt": "Desfazer remoção do grupo",
|
||||
"undo": "Desfazer"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Adicionar por URL",
|
||||
"title": "Adicionar um grupo por URL",
|
||||
"description": "Se um grupo foi compartilhado com você, você pode colar sua URL aqui para adicioná-lo à sua lista.",
|
||||
"error": "Ops, não conseguimos encontrar o grupo a partir da URL fornecida…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Este grupo não existe.",
|
||||
"link": "Ir para grupos visitados recentemente"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informações do grupo",
|
||||
"NameField": {
|
||||
"label": "Nome do grupo",
|
||||
"placeholder": "Férias de verão",
|
||||
"description": "Insira um nome para o seu grupo."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informações do grupo",
|
||||
"placeholder": "Quais informações são relevantes para os participantes do grupo?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Símbolo da moeda",
|
||||
"placeholder": "$, €, £, R$…",
|
||||
"description": "Vamos usá-lo para exibir valores."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participantes",
|
||||
"description": "Insira o nome de cada participante.",
|
||||
"protectedParticipant": "Este participante faz parte das despesas e não pode ser removido.",
|
||||
"new": "Novo",
|
||||
"add": "Adicionar participante",
|
||||
"John": "João",
|
||||
"Jane": "Maria",
|
||||
"Jack": "José"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Configurações locais",
|
||||
"description": "Essas configurações são definidas por dispositivo e são usadas para personalizar sua experiência.",
|
||||
"ActiveUserField": {
|
||||
"label": "Usuário ativo",
|
||||
"placeholder": "Selecione um participante",
|
||||
"none": "Nenhum",
|
||||
"description": "Usuário usado como padrão para pagar despesas."
|
||||
},
|
||||
"save": "Salvar",
|
||||
"saving": "Salvando…",
|
||||
"create": "Criar",
|
||||
"creating": "Criando…",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Moeda principal",
|
||||
"createDescription": "Todos os valores e saldos estarão nesta moeda.",
|
||||
"editDescription": "Todos os valores e saldos estarão nesta moeda. A sua alteração NÃO irá converter despesas já registradas, exceto quando a moeda possuir \"unidades menores\" que a atual (ex. Alterar de Dólar Americano para Yen Japonês)",
|
||||
"customOption": "Customizado"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Criar receita",
|
||||
"edit": "Editar receita",
|
||||
"TitleField": {
|
||||
"label": "Título da receita",
|
||||
"placeholder": "Restaurante na segunda à noite",
|
||||
"description": "Insira uma descrição para a receita."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data da receita",
|
||||
"description": "Insira a data em que a receita foi recebida."
|
||||
},
|
||||
"categoryFieldDescription": "Selecione a categoria da receita.",
|
||||
"paidByField": {
|
||||
"label": "Recebido por",
|
||||
"description": "Selecione o participante que recebeu a receita."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Recebido para",
|
||||
"description": "Selecione para quem a receita foi recebida."
|
||||
},
|
||||
"splitModeDescription": "Selecione como dividir a receita.",
|
||||
"attachDescription": "Veja e anexe recibos à receita."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Criar despesa",
|
||||
"edit": "Editar despesa",
|
||||
"TitleField": {
|
||||
"label": "Título da despesa",
|
||||
"placeholder": "Restaurante na segunda à noite",
|
||||
"description": "Insira uma descrição para a despesa."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data da despesa",
|
||||
"description": "Insira a data em que a despesa foi paga."
|
||||
},
|
||||
"categoryFieldDescription": "Selecione a categoria da despesa.",
|
||||
"paidByField": {
|
||||
"label": "Pago por",
|
||||
"description": "Selecione o participante que pagou a despesa.",
|
||||
"placeholder": "Selecione um participante"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Pago para",
|
||||
"description": "Selecione para quem a despesa foi paga."
|
||||
},
|
||||
"splitModeDescription": "Selecione como dividir a despesa.",
|
||||
"attachDescription": "Veja e anexe recibos à despesa.",
|
||||
"recurrenceRule": {
|
||||
"label": "Recorrência da Despesa",
|
||||
"description": "Selecione a frequência de recorrência da despesa.",
|
||||
"none": "Nenhuma",
|
||||
"daily": "Diariamente",
|
||||
"weekly": "Semanalmente",
|
||||
"monthly": "Mensalmente"
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Valor"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Isso é um reembolso"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categoria"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notas"
|
||||
},
|
||||
"selectNone": "Remover seleção",
|
||||
"selectAll": "Selecionar todos(as)",
|
||||
"shares": "parte(s)",
|
||||
"advancedOptions": "Opções avançadas de divisão…",
|
||||
"SplitModeField": {
|
||||
"label": "Modo de divisão",
|
||||
"evenly": "Igualmente",
|
||||
"byShares": "Desigualmente - Por partes",
|
||||
"byPercentage": "Desigualmente - Por porcentagem",
|
||||
"byAmount": "Desigualmente - Por valor",
|
||||
"saveAsDefault": "Salvar como opções de divisão padrão"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Excluir",
|
||||
"title": "Excluir esta despesa?",
|
||||
"description": "Você realmente deseja excluir esta despesa? Esta ação é irreversível.",
|
||||
"yes": "Sim",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"attachDocuments": "Anexar documentos",
|
||||
"create": "Criar",
|
||||
"creating": "Criando…",
|
||||
"save": "Salvar",
|
||||
"saving": "Salvando…",
|
||||
"cancel": "Cancelar",
|
||||
"reimbursement": "Reembolso",
|
||||
"conversionRateField": {
|
||||
"label": "Taxa de câmbio"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"success": "Taxas obtidas:",
|
||||
"error": "Opa, não conseguimos obter as taxas mais recentes.",
|
||||
"refresh": "Atualizar"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "O arquivo é muito grande",
|
||||
"description": "O tamanho máximo de arquivo que você pode enviar é {maxSize}. O seu é ${size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Erro ao enviar documento",
|
||||
"description": "Algo deu errado ao enviar o documento. Por favor, tente novamente mais tarde ou selecione um arquivo diferente.",
|
||||
"retry": "Tentar novamente"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Criar despesa a partir de recibo",
|
||||
"title": "Criar a partir de recibo",
|
||||
"description": "Extraia as informações da despesa a partir de uma foto de recibo.",
|
||||
"body": "Faça upload da foto de um recibo, e vamos escaneá-la para extrair as informações da despesa, se possível.",
|
||||
"selectImage": "Selecionar imagem…",
|
||||
"titleLabel": "Título:",
|
||||
"categoryLabel": "Categoria:",
|
||||
"amountLabel": "Valor:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "Você poderá editar as informações da despesa a seguir.",
|
||||
"continue": "Continuar"
|
||||
},
|
||||
"unknown": "Desconhecido",
|
||||
"TooBigToast": {
|
||||
"title": "O arquivo é muito grande",
|
||||
"description": "O tamanho máximo de arquivo que você pode enviar é {maxSize}. O seu é ${size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Erro ao enviar documento",
|
||||
"description": "Algo deu errado ao enviar o documento. Por favor, tente novamente mais tarde ou selecione um arquivo diferente.",
|
||||
"retry": "Tentar novamente"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Saldos",
|
||||
"description": "Este é o valor que cada participante pagou ou recebeu.",
|
||||
"Reimbursements": {
|
||||
"title": "Reembolsos sugeridos",
|
||||
"description": "Aqui estão sugestões para reembolsos otimizados entre os participantes.",
|
||||
"noImbursements": "Parece que seu grupo não precisa de nenhum reembolso 😁",
|
||||
"owes": "<strong>{from}</strong> deve <strong>{to}</strong>",
|
||||
"markAsPaid": "Marcar como pago"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Estatísticas",
|
||||
"Totals": {
|
||||
"title": "Totais",
|
||||
"description": "Resumo de gastos de todo o grupo.",
|
||||
"groupSpendings": "Total de gastos do grupo",
|
||||
"groupEarnings": "Total de receitas do grupo",
|
||||
"yourSpendings": "Seus gastos totais",
|
||||
"yourEarnings": "Suas receitas totais",
|
||||
"yourShare": "Sua participação total"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Atividade",
|
||||
"description": "Visão geral de toda a atividade neste grupo.",
|
||||
"noActivity": "Ainda não há atividades no seu grupo.",
|
||||
"someone": "Alguém",
|
||||
"settingsModified": "As configurações do grupo foram modificadas por <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Despesa {expense} criada por <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Despesa {expense} atualizada por <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Despesa {expense} excluída por <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Hoje",
|
||||
"yesterday": "Ontem",
|
||||
"earlierThisWeek": "Anteriormente nesta semana",
|
||||
"lastWeek": "Semana passada",
|
||||
"earlierThisMonth": "Anteriormente neste mês",
|
||||
"lastMonth": "Mês passado",
|
||||
"earlierThisYear": "Anteriormente neste ano",
|
||||
"lastYear": "Ano passado",
|
||||
"older": "Mais antigas"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informação",
|
||||
"description": "Use este espaço para adicionar qualquer informação que possa ser relevante para os participantes do grupo.",
|
||||
"empty": "Nenhuma informação do grupo ainda."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Configurações"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Compartilhar",
|
||||
"description": "Para que outros participantes vejam o grupo e adicionem despesas, compartilhe o link com eles.",
|
||||
"warning": "Aviso!",
|
||||
"warningHelp": "Toda pessoa com o link do grupo poderá ver e editar despesas. Compartilhe com cautela!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Digite pelo menos um caractere.",
|
||||
"min2": "Digite pelo menos dois caracteres.",
|
||||
"max5": "Digite no máximo cinco caracteres.",
|
||||
"max50": "Digite no máximo 50 caracteres.",
|
||||
"duplicateParticipantName": "Outro participante já tem este nome.",
|
||||
"titleRequired": "Por favor, insira um título.",
|
||||
"invalidNumber": "Número inválido.",
|
||||
"amountRequired": "Você deve inserir um valor.",
|
||||
"amountNotZero": "O valor não deve ser zero.",
|
||||
"amountTenMillion": "O valor deve ser inferior a 10.000.000.",
|
||||
"paidByRequired": "Você deve selecionar um participante.",
|
||||
"paidForMin1": "A despesa deve ser paga para pelo menos um participante.",
|
||||
"noZeroShares": "Todas as partes devem ser maiores que 0.",
|
||||
"amountSum": "A soma dos valores deve ser igual ao valor da despesa.",
|
||||
"percentageSum": "A soma das porcentagens deve ser igual a 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Pesquisar categoria...",
|
||||
"noCategory": "Nenhuma categoria encontrada.",
|
||||
"Uncategorized": {
|
||||
"heading": "Sem categoria",
|
||||
"General": "Geral",
|
||||
"Payment": "Pagamento"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Entretenimento",
|
||||
"Entertainment": "Entretenimento",
|
||||
"Games": "Jogos",
|
||||
"Movies": "Filmes",
|
||||
"Music": "Música",
|
||||
"Sports": "Esportes"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Comida e Bebida",
|
||||
"Food and Drink": "Comida e Bebida",
|
||||
"Dining Out": "Jantar fora",
|
||||
"Groceries": "Mercearia",
|
||||
"Liquor": "Bebidas alcoólicas"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Casa",
|
||||
"Home": "Casa",
|
||||
"Electronics": "Eletrônicos",
|
||||
"Furniture": "Móveis",
|
||||
"Household Supplies": "Suprimentos domésticos",
|
||||
"Maintenance": "Manutenção",
|
||||
"Mortgage": "Financiamento Habitacional",
|
||||
"Pets": "Animais de estimação",
|
||||
"Rent": "Aluguel",
|
||||
"Services": "Serviços"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Vida",
|
||||
"Childcare": "Cuidados infantis",
|
||||
"Clothing": "Roupas",
|
||||
"Education": "Educação",
|
||||
"Gifts": "Presentes",
|
||||
"Insurance": "Seguro",
|
||||
"Medical Expenses": "Despesas médicas",
|
||||
"Taxes": "Impostos",
|
||||
"Donation": "Doação"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transporte",
|
||||
"Transportation": "Transporte",
|
||||
"Bicycle": "Bicicleta",
|
||||
"Bus/Train": "Ônibus/Trem",
|
||||
"Car": "Carro",
|
||||
"Gas/Fuel": "Gasolina/Combustível",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Estacionamento",
|
||||
"Plane": "Avião",
|
||||
"Taxi": "Táxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilitários",
|
||||
"Utilities": "Utilitários",
|
||||
"Cleaning": "Limpeza",
|
||||
"Electricity": "Eletricidade",
|
||||
"Heat/Gas": "Calor/Gás",
|
||||
"Trash": "Lixo",
|
||||
"TV/Phone/Internet": "TV/Telefone/Internet",
|
||||
"Water": "Água"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "Pesquisar moeda...",
|
||||
"noCurrency": "Nenhuma moeda encontrada.",
|
||||
"custom": {
|
||||
"heading": "Customizado"
|
||||
},
|
||||
"common": {
|
||||
"heading": "Mais comum"
|
||||
},
|
||||
"other": {
|
||||
"heading": "Outras moedas"
|
||||
}
|
||||
}
|
||||
}
|
||||
127
messages/pt.json
Normal file
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Partilha <strong>Despesas</strong> com <strong>Amigos e Família</strong>",
|
||||
"description": "Bem-vindo/a à tua nova instância <strong>Spliit</strong> !",
|
||||
"button": {
|
||||
"groups": "Ir para grupos",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grupos"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Feito em Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Construído por <author>Sebastien Castiel</author> e <source>contribuidores</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Despesas",
|
||||
"description": "Aqui estão as despesas que criaste para o teu grupo.",
|
||||
"create": "Criar despesa",
|
||||
"createFirst": "Criar a primeira",
|
||||
"noExpenses": "O teu grupo ainda não tem nenhuma despesa.",
|
||||
"export": "Exportar",
|
||||
"exportJson": "Exportar para JSON",
|
||||
"exportCsv": "Exportar para CSV",
|
||||
"searchPlaceholder": "Pesquisar uma despesa…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Quem és?",
|
||||
"description": "Diz-nos que participante és para nos permitir customizar como a informação é apresentada.",
|
||||
"nobody": "Não quero selecionar ninguém",
|
||||
"save": "Guardar alterações",
|
||||
"footer": "Esta definição pode ser alterada posteriormente nas definições do grupo."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Em breve",
|
||||
"thisWeek": "Esta semana",
|
||||
"earlierThisMonth": "No início deste mês",
|
||||
"lastMonth": "Mês passado",
|
||||
"earlierThisYear": "No início deste ano",
|
||||
"lastYear": "Ano passado",
|
||||
"older": "Mais antigos"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Pago por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"everyone": "todos",
|
||||
"receivedBy": "Recebido por <strong>{paidBy}</strong> para <paidFor></paidFor>",
|
||||
"yourBalance": "O teu saldo:",
|
||||
"notInvolved": "Não participaste"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Os meus grupos",
|
||||
"create": "Criar",
|
||||
"loadingRecent": "A carregar grupos recentes…",
|
||||
"NoRecent": {
|
||||
"description": "Não visitaste nenhum grupo recentemente.",
|
||||
"create": "Criar um",
|
||||
"orAsk": "ou pergunta a um amigo para te mandar o link de um já existente."
|
||||
},
|
||||
"recent": "Grupos recentes",
|
||||
"starred": "Grupos favoritos",
|
||||
"archived": "Grupos arquivados",
|
||||
"archive": "Arquivar grupo",
|
||||
"unarchive": "Desarquivar grupo",
|
||||
"removeRecent": "Remover dos grupos recentes",
|
||||
"RecentRemovedToast": {
|
||||
"title": "O grupo foi removido",
|
||||
"description": "O grupo foi removido da tua lista de grupos recentes.",
|
||||
"undoAlt": "Desfazer remoção do grupo",
|
||||
"undo": "Desfazer"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Adicionar por URL",
|
||||
"title": "Adicionar um grupo por URL",
|
||||
"description": "Se um grupo foi partilhado contigo, podes colar o URL aqui para o adicionar à tua lista.",
|
||||
"error": "Oops, não conseguimos encontrar o grupo pelo URL que indicaste…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Este grupo não existe.",
|
||||
"link": "Ir para os grupos visitados recentemente"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informações do grupo",
|
||||
"NameField": {
|
||||
"label": "Nome do grupo",
|
||||
"placeholder": "Férias de verão",
|
||||
"description": "Introduz um nome para o teu grupo."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informações do grupo",
|
||||
"placeholder": "Que informações são relevantes para os participantes do grupo?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Símbolo da moeda",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Utilizá-lo-emos para mostrar montantes."
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "Moeda principal",
|
||||
"createDescription": "Todos os montantes e saldos vão estar nesta moeda.",
|
||||
"editDescription": "Todos os montantes e saldos vão estar nesta moeda. Ao alterares isto NÃO vão ser convertidas despesas já inseridas, excepto quando a moeda tem \"unidades monetárias menores\" diferentes da atual (ex. alterar de Dólar Americano para Yen Japonês)",
|
||||
"customOption": "Customizado"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Participantes",
|
||||
"description": "Introduz o nome de cada participante.",
|
||||
"protectedParticipant": "Este participante faz parte de despesas e não pode ser removido.",
|
||||
"new": "Novo",
|
||||
"add": "Adicionar participante",
|
||||
"John": "João",
|
||||
"Jane": "Joana",
|
||||
"Jack": "Joaquim"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Definições locais",
|
||||
"description": "Estas definições são definidas por dispositivo, e são utilizadas para customizar a tua experiência.",
|
||||
"ActiveUserField": {
|
||||
"label": "Utilizador ativo",
|
||||
"placeholder": "Selecionar um participante",
|
||||
"none": "Nenhum",
|
||||
"description": "Utilizador usado por defeito para pagar despesas."
|
||||
},
|
||||
"save": "Gravar"
|
||||
}
|
||||
}
|
||||
}
|
||||
389
messages/ro.json
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Distribuie <strong>Cheltuielile</strong> cu <strong>Prietenii & Familia</strong>",
|
||||
"description": "Bine ai venit pe noua ta instanță de <strong>Spliit</strong> !",
|
||||
"button": {
|
||||
"groups": "Mergi la grupuri",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Grupuri"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Dezvoltat în Montréal, Québec 🇨🇦",
|
||||
"builtBy": "Dezvoltat de către <author>Sebastien Castiel</author> și <source>contribuitori</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Cheltuieli",
|
||||
"description": "Aici sunt cheltuielile pe care le-ai creat pentru grupul tău.",
|
||||
"create": "Adaugă o cheltuială",
|
||||
"createFirst": "Adaug-o pe prima",
|
||||
"noExpenses": "Grupul tău nu conține nicio cheltuială încă.",
|
||||
"exportJson": "Salvează în JSON",
|
||||
"exportCsv": "Salvează în CSV",
|
||||
"searchPlaceholder": "Caută o cheltuială…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Cum te numești?",
|
||||
"description": "Spune-ne cine ești ca să putem îți afișăm informațiile relevante.",
|
||||
"nobody": "Nu doresc să aleg pe nimeni",
|
||||
"save": "Salvează",
|
||||
"footer": "Această setare se poate schimba mai târziu din setările grupului."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Urmează",
|
||||
"thisWeek": "În această săptămână",
|
||||
"earlierThisMonth": "La începutul lunii",
|
||||
"lastMonth": "Luna trecută",
|
||||
"earlierThisYear": "La începutul anului",
|
||||
"lastYear": "Anul trecut",
|
||||
"older": "Mai vechi"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Plătit de <strong>{paidBy}</strong> pentru <paidFor></paidFor>",
|
||||
"receivedBy": "Primit de <strong>{paidBy}</strong> pentru <paidFor></paidFor>",
|
||||
"yourBalance": "Soldul tău:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Grupurile mele",
|
||||
"create": "Adaugă",
|
||||
"loadingRecent": "Se încarcă ultimele tale grupuri…",
|
||||
"NoRecent": {
|
||||
"description": "Nu ai accesat niciun grup recent.",
|
||||
"create": "Adaugă unul",
|
||||
"orAsk": "sau roagă un prieten să îți trimită un link către unul deja existent."
|
||||
},
|
||||
"recent": "Ultimele grupuri",
|
||||
"starred": "Grupuri favorite",
|
||||
"archived": "Grupuri arhivate",
|
||||
"archive": "Arhivează grupul",
|
||||
"unarchive": "Dezarhivează grupul",
|
||||
"removeRecent": "Șterge din ultimele grupuri",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Grupul a fost șters.",
|
||||
"description": "Grupul a fost șters din lista ta de grupuri recente.",
|
||||
"undoAlt": "Anulează ștergerea grupului",
|
||||
"undo": "Anulează"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Adaugă folosind un URL",
|
||||
"title": "Adaugă un grup folosind un URL",
|
||||
"description": "Dacă un grup a fost distribuit cu tine, poți atașa URL-ul acestuia aici pentru a-l adăuga în listă.",
|
||||
"error": "Ups, nu am găsit grupul folosind URL-ul primit de la tine…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Acest grup nu există.",
|
||||
"link": "Mergi la ultimele grupuri vizitate"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Informații despre grup",
|
||||
"NameField": {
|
||||
"label": "Numele grupului",
|
||||
"placeholder": "Vacanță de vară",
|
||||
"description": "Adaugă un nume pentru grupul tău."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Informații despre grup",
|
||||
"placeholder": "Ce informație este relevantă pentru membrii grupului?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Monedă",
|
||||
"placeholder": "$, €, £, RON …",
|
||||
"description": "O vom folosi pentru a afișa sume."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Membri",
|
||||
"description": "Adaugă numele fiecărui membru.",
|
||||
"protectedParticipant": "Acest membru a luat parte la cheltuieli și nu poate să fie șters.",
|
||||
"new": "Nou",
|
||||
"add": "Adaugă membru",
|
||||
"John": "John",
|
||||
"Jane": "Jane",
|
||||
"Jack": "Jack"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Setări locale",
|
||||
"description": "Aceste setări sunt făcute pentru fiecare dispozitiv și sunt folosite pentru a-ți personaliza experiența.",
|
||||
"ActiveUserField": {
|
||||
"label": "Utilizator activ",
|
||||
"placeholder": "Selectează un membru",
|
||||
"none": "Niciunul",
|
||||
"description": "Utilizatorul implicit pentru plata cheltuielilor."
|
||||
},
|
||||
"save": "Salvează",
|
||||
"saving": "Se salvează…",
|
||||
"create": "Adaugă",
|
||||
"creating": "Se adaugă…",
|
||||
"cancel": "Anulează"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Adaugă un venit",
|
||||
"edit": "Modifică venitul",
|
||||
"TitleField": {
|
||||
"label": "Titlul venitului",
|
||||
"placeholder": "Cina de luni seară",
|
||||
"description": "Adaugă o descriere pentru venit."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data venitului",
|
||||
"description": "Adaugă data la care venitul a fost primit."
|
||||
},
|
||||
"categoryFieldDescription": "Selectează categoria venitului.",
|
||||
"paidByField": {
|
||||
"label": "Primit de către",
|
||||
"description": "Selectează membrul care a primit venitul."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Primit pentru",
|
||||
"description": "Selectează pentru cine a fost primit venitul."
|
||||
},
|
||||
"splitModeDescription": "Selectează cum să fie împărțit venitul.",
|
||||
"attachDescription": "Vizualizează și atașează bonul pentru venit."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Adaugă o cheltuială",
|
||||
"edit": "Modifică cheltuiala",
|
||||
"TitleField": {
|
||||
"label": "Titlul cheltuielii",
|
||||
"placeholder": "Cina de luni seară",
|
||||
"description": "Adaugă o descriere pentru cheltuială."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Data cheltuielii",
|
||||
"description": "Adaugă data la care cheltuiala a fost facută."
|
||||
},
|
||||
"categoryFieldDescription": "Selectează categoria cheltuielii.",
|
||||
"paidByField": {
|
||||
"label": "Plătit de către",
|
||||
"description": "Selectează membrul care a plătit cheltuiala."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Plătit pentru",
|
||||
"description": "Selectează pentru cine a fost platită cheltuiala."
|
||||
},
|
||||
"splitModeDescription": "Selectează cum să fie împărțită cheltuiala.",
|
||||
"attachDescription": "Vizualizează și atașează bonul pentru cheltuială."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Sumă"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Aceasta este o rambursare."
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Categorie"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notițe"
|
||||
},
|
||||
"selectNone": "Nu selectez nimic",
|
||||
"selectAll": "Selectez tot",
|
||||
"shares": "distribuiri",
|
||||
"advancedOptions": "Opțiuni avansate de împărțire…",
|
||||
"SplitModeField": {
|
||||
"label": "Împărțire",
|
||||
"evenly": "Egal",
|
||||
"byShares": "Inegal – În funcție de parte",
|
||||
"byPercentage": "Inegal – În funcție de procentaj",
|
||||
"byAmount": "Inegal – În funcție de sumă",
|
||||
"saveAsDefault": "Salvează ca și implicite opțiunile de împărțire"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Șterge",
|
||||
"title": "Ștergi această cheltuială?",
|
||||
"description": "Ești sigur că vrei să ștergi această cheltuială? Această acțiune este ireversibilă.",
|
||||
"yes": "Da",
|
||||
"cancel": "Anulează"
|
||||
},
|
||||
"attachDocuments": "Atașează documente",
|
||||
"create": "Adaugă",
|
||||
"creating": "Se adaugă…",
|
||||
"save": "Salvează",
|
||||
"saving": "Se salvează…",
|
||||
"cancel": "Anulează",
|
||||
"reimbursement": "Rambursare"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Fișierul este prea mare",
|
||||
"description": "Dimensiunea maximă a fișierului pe care îl poți atașa este {maxSize}. Fișierul tău are {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Eroare la adăugarea documentului.",
|
||||
"description": "Ceva a mers greșit la adăugarea fișierului. Încearcă mai târziu sau cum un alt fișier.",
|
||||
"retry": "Reîncearcă"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Adaugă o cheltuială dintr-un bon",
|
||||
"title": "Adaugă din bon",
|
||||
"description": "Extrage informații despre cheltuială dintr-o poză cu bonul.",
|
||||
"body": "Adaugă o poză cu bonul și vom încerca să o scanăm pentru a extrage informații despre cheltuială.",
|
||||
"selectImage": "Selectează o imagine…",
|
||||
"titleLabel": "Titlu:",
|
||||
"categoryLabel": "Categorie:",
|
||||
"amountLabel": "Sumă:",
|
||||
"dateLabel": "Data:",
|
||||
"editNext": "Vei putea sa modifici informațiile despre cheltuială în continuare.",
|
||||
"continue": "Continuă"
|
||||
},
|
||||
"unknown": "Necunoscut",
|
||||
"TooBigToast": {
|
||||
"title": "Fișierul este prea mare",
|
||||
"description": "Dimensiunea maximă a fișierului pe care il poți atașa este {maxSize}. Fișierul tău are {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Eroare la adăugarea documentului.",
|
||||
"description": "Ceva a mers greșit la adăugarea fișierului. Încearcă mai târziu sau cum un alt fișier.",
|
||||
"retry": "Reîncearcă"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Solduri",
|
||||
"description": "Aceasta este suma pe care fiecare membru a plătit-o sau cu care a fost plătit.",
|
||||
"Reimbursements": {
|
||||
"title": "Rambursări sugerate",
|
||||
"description": "Acestea sunt sugestiile pentru rambursări optimizate între membrii.",
|
||||
"noImbursements": "Se pare că grupul tău nu are nevoie de rambursări 😁",
|
||||
"owes": "<strong>{from}</strong> datorează <strong>{to}</strong>",
|
||||
"markAsPaid": "Bifează ca plătit"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Statistici",
|
||||
"Totals": {
|
||||
"title": "Totaluri",
|
||||
"description": "Sumarul cheltuielior pentru întregul grup.",
|
||||
"groupSpendings": "Totalul cheltuielilor din grup",
|
||||
"groupEarnings": "Totalul veniturilor din grup",
|
||||
"yourSpendings": "Totalul cheltuielilor tale",
|
||||
"yourEarnings": "Totalul veniturilor tale",
|
||||
"yourShare": "Partea ta"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Activități",
|
||||
"description": "Rezumatul întregii activități a grupului.",
|
||||
"noActivity": "Nu este nicio activitate în grupul tău încă.",
|
||||
"someone": "Cineva",
|
||||
"settingsModified": "Setările grupului au fost modificate de <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Cheltuială <em>{expense}</em> adăugată de <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Cheltuială <em>{expense}</em> modificată de <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Cheltuială <em>{expense}</em> ștearsă de <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Azi",
|
||||
"yesterday": "Ieri",
|
||||
"earlierThisWeek": "La începutul săptămânii",
|
||||
"lastWeek": "Săptămâna trecută",
|
||||
"earlierThisMonth": "La începutul lunii",
|
||||
"lastMonth": "Luna trecuta",
|
||||
"earlierThisYear": "La începutul anului",
|
||||
"lastYear": "Anul trecut",
|
||||
"older": "Mai vechi"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Informații",
|
||||
"description": "Adaugă aici orice informație care poate să fie relevantă pentru membrii grupului.",
|
||||
"empty": "Nicio informație de grup încă."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Setări"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Distribuie",
|
||||
"description": "Pentru ca ceilalți participanți să poată vedea grupul și cheltuielile adăugate, distribuie URL-ul acestuia cu ei.",
|
||||
"warning": "Avertisment!",
|
||||
"warningHelp": "Oricine are URL-ul grupului va putea să vadă și să editeze cheltuielile. Distribuie cu grijă!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Introduceți cel puțin un caracter.",
|
||||
"min2": "Introduceți cel puțin două caractere.",
|
||||
"max5": "Introduceți cel mult cinci caractere.",
|
||||
"max50": "Introduceți cel mult 50 de caractere.",
|
||||
"duplicateParticipantName": "Un alt membru are deja acest nume.",
|
||||
"titleRequired": "Vă rugăm să introduceți un titlu.",
|
||||
"invalidNumber": "Număr invalid.",
|
||||
"amountRequired": "Trebuie să introduceți o sumă.",
|
||||
"amountNotZero": "Suma nu trebuie să fie zero.",
|
||||
"amountTenMillion": "Suma trebuie să fie mai mică de 10,000,000.",
|
||||
"paidByRequired": "Trebuie să selectați un membru.",
|
||||
"paidForMin1": "Cheltuiala trebuie plătită pentru cel puțin un membru.",
|
||||
"noZeroShares": "Toate părțile trebuie să fie mai mari de 0.",
|
||||
"amountSum": "Suma valorilor trebuie să fie egală cu suma cheltuielilor.",
|
||||
"percentageSum": "Suma procentajelor trebuie să fie egală cu 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Căutați categorie…",
|
||||
"noCategory": "Nicio categorie găsită.",
|
||||
"Uncategorized": {
|
||||
"heading": "Fără categorie",
|
||||
"General": "General",
|
||||
"Payment": "Plată"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Divertisment",
|
||||
"Entertainment": "Divertisment",
|
||||
"Games": "Jocuri",
|
||||
"Movies": "Filme",
|
||||
"Music": "Muzică",
|
||||
"Sports": "Sporturi"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Mâncare și Băutură",
|
||||
"Food and Drink": "Mâncare și Băutură",
|
||||
"Dining Out": "Cină în oraș",
|
||||
"Groceries": "Alimente",
|
||||
"Liquor": "Băuturi alcoolice"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Acasă",
|
||||
"Home": "Acasă",
|
||||
"Electronics": "Electronice",
|
||||
"Furniture": "Mobilier",
|
||||
"Household Supplies": "Produse de uz casnic",
|
||||
"Maintenance": "Întreținere",
|
||||
"Mortgage": "Ipotecă",
|
||||
"Pets": "Animale de companie",
|
||||
"Rent": "Chirie",
|
||||
"Services": "Servicii"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Viață",
|
||||
"Childcare": "Îngrijirea copiilor",
|
||||
"Clothing": "Îmbrăcăminte",
|
||||
"Education": "Educație",
|
||||
"Gifts": "Cadouri",
|
||||
"Insurance": "Asigurare",
|
||||
"Medical Expenses": "Cheltuieli medicale",
|
||||
"Taxes": "Impozite"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Transport",
|
||||
"Transportation": "Transport",
|
||||
"Bicycle": "Bicicletă",
|
||||
"Bus/Train": "Autobuz/Tren",
|
||||
"Car": "Mașină",
|
||||
"Gas/Fuel": "Gaz/Combustibil",
|
||||
"Hotel": "Hotel",
|
||||
"Parking": "Parcare",
|
||||
"Plane": "Avion",
|
||||
"Taxi": "Taxi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Utilități",
|
||||
"Utilities": "Utilități",
|
||||
"Cleaning": "Curățenie",
|
||||
"Electricity": "Electricitate",
|
||||
"Heat/Gas": "Încălzire/Gaz",
|
||||
"Trash": "Gunoi",
|
||||
"TV/Phone/Internet": "TV/Telefon/Internet",
|
||||
"Water": "Apă"
|
||||
}
|
||||
}
|
||||
}
|
||||
390
messages/ru-RU.json
Normal file
@@ -0,0 +1,390 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Делитесь <strong>расходами</strong> с <strong>друзьями и семьей</strong>",
|
||||
"description": "Добро пожаловать в вашу новую инстанцию <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Перейти к группам",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Группы"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Сделано в Монреале, Квебек 🇨🇦",
|
||||
"builtBy": "Создано <author>Sebastien Castiel</author> и <source>соавторами</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Расходы",
|
||||
"description": "В этом разделе находятся расходы вашей группы.",
|
||||
"create": "Создать расход",
|
||||
"createFirst": "Создать первый расход",
|
||||
"noExpenses": "У вашей группы пока что нет расходов.",
|
||||
"exportJson": "Экспортировать в JSON",
|
||||
"exportCsv": "Экспортировать в CSV",
|
||||
"searchPlaceholder": "Поиск расходов…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Кто вы?",
|
||||
"description": "Скажите нам, кто вы из этого списка, чтобы мы могли подстроить интерфейс под вас.",
|
||||
"nobody": "Не хочу выбирать",
|
||||
"save": "Сохранить изменения",
|
||||
"footer": "Вы сможете изменить это в настройках группы."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Будущее",
|
||||
"thisWeek": "На этой неделе",
|
||||
"earlierThisMonth": "Ранее в этом месяце",
|
||||
"lastMonth": "В прошлом месяце",
|
||||
"earlierThisYear": "Ранее в этом году",
|
||||
"lastYear": "В прошлом году",
|
||||
"older": "Очень давно"
|
||||
},
|
||||
"export": "Экспортировать"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Потратил <strong>{paidBy}</strong> за <paidFor></paidFor>",
|
||||
"receivedBy": "Получил <strong>{paidBy}</strong> за <paidFor></paidFor>",
|
||||
"yourBalance": "Изменение баланса участника:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Мои группы",
|
||||
"create": "Создать",
|
||||
"loadingRecent": "Загрузка недавних групп…",
|
||||
"NoRecent": {
|
||||
"description": "У вас нет недавних групп.",
|
||||
"create": "Вы можете создать группу",
|
||||
"orAsk": "или попросить вашего друга отправить вам ссылку на существующую."
|
||||
},
|
||||
"recent": "Недавние группы",
|
||||
"starred": "Избранные",
|
||||
"archived": "Архивированные группы",
|
||||
"archive": "Архивировать группу",
|
||||
"unarchive": "Восстановить группу",
|
||||
"removeRecent": "Убрать группу из недавних",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Группа убрана",
|
||||
"description": "Группа была убрана из вашего списка недавних групп.",
|
||||
"undoAlt": "Отменить удаление группы из этого списка",
|
||||
"undo": "Отмена"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Добавить по URL",
|
||||
"title": "Добавить группу по URL",
|
||||
"description": "Если с вами поделились ссылкой на группу, вставьте ее сюда, чтобы добавить ее в ваш список.",
|
||||
"error": "К сожалению, мы не смогли найти группу по этому URL."
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Этой группы не существует",
|
||||
"link": "Перейти к списку недавних групп"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Сведения о группе",
|
||||
"NameField": {
|
||||
"label": "Название группы",
|
||||
"placeholder": "Летние поездки",
|
||||
"description": "Введите название вашей группы."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Информация о группе",
|
||||
"placeholder": "Что важно знать участникам этой группы?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Символ валюты",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Этот символ будет использован для отображений денежных сумм."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Участники",
|
||||
"description": "Введите имя каждого участника.",
|
||||
"protectedParticipant": "Этот участник — часть расходов, и поэтому не может быть удален.",
|
||||
"new": "Новый участник",
|
||||
"add": "Добавить участника",
|
||||
"John": "Александр",
|
||||
"Jane": "Михаил",
|
||||
"Jack": "Иван"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Локальные настройки",
|
||||
"description": "Эти настройки хранятся на вашем устройстве и используются для подстройки интерфейса для вас.",
|
||||
"ActiveUserField": {
|
||||
"label": "Активный участник",
|
||||
"placeholder": "Выберите участника",
|
||||
"none": "Не выбран",
|
||||
"description": "Этот участник будет автоматически выбран при создании нового расхода."
|
||||
},
|
||||
"save": "Сохранить",
|
||||
"saving": "Сохранение…",
|
||||
"create": "Создать",
|
||||
"creating": "Создание…",
|
||||
"cancel": "Отмена"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Создать доход",
|
||||
"edit": "Изменить доход",
|
||||
"TitleField": {
|
||||
"label": "Название доходв",
|
||||
"placeholder": "Поход в ресторан",
|
||||
"description": "Введите описание для этого дохода."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Дата дохода",
|
||||
"description": "Введите дату, когда этот доход был получен."
|
||||
},
|
||||
"categoryFieldDescription": "Выберите категорию дохода.",
|
||||
"paidByField": {
|
||||
"label": "Получивший",
|
||||
"description": "Выберите участника, который получил этот доход."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Участники",
|
||||
"description": "Выберите тех, между кем этот доход будет распределен."
|
||||
},
|
||||
"splitModeDescription": "Выберите, как доход необходимо распределить между людьми.",
|
||||
"attachDescription": "Просмотр и прикрепление чеков к этому расходу."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Создать расход",
|
||||
"edit": "Изменить расход",
|
||||
"TitleField": {
|
||||
"label": "Название расхода",
|
||||
"placeholder": "Поход в ресторан",
|
||||
"description": "Введите описание для этого расхода."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Дата расхода",
|
||||
"description": "Введите дату, когда этот расход был совершен."
|
||||
},
|
||||
"categoryFieldDescription": "Выберите категорию расхода.",
|
||||
"paidByField": {
|
||||
"label": "Оплативший",
|
||||
"description": "Выберите участника, который оплатил этот расход."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Участники",
|
||||
"description": "Выберите тех, между кем этот расход будет распределен. Если этот расход — возмещение участнику (участникам), выберите только его (их)."
|
||||
},
|
||||
"splitModeDescription": "Выберите, как расход необходимо распределить между людьми.",
|
||||
"attachDescription": "Просмотр и прикрепление чеков к этому доходу."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Сумма"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Этот расход является возмещением"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Категория"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Заметки"
|
||||
},
|
||||
"selectNone": "Выбрать никого",
|
||||
"selectAll": "Выбрать всех",
|
||||
"shares": "доля(и)",
|
||||
"advancedOptions": "Дополнительные настройки распределения…",
|
||||
"SplitModeField": {
|
||||
"label": "Режим разделения",
|
||||
"evenly": "Равный",
|
||||
"byShares": "Неравный – По долям",
|
||||
"byPercentage": "Неравный – По процентам",
|
||||
"byAmount": "Неравный – По суммам",
|
||||
"saveAsDefault": "Сделать режимом по умолчанию"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Удалить",
|
||||
"title": "Удалить этот расход?",
|
||||
"description": "Вы действительно хотите удалить этот расход? Это действие нельзя отменить.",
|
||||
"yes": "Удалить",
|
||||
"cancel": "Отмена"
|
||||
},
|
||||
"attachDocuments": "Прикрепить документы",
|
||||
"create": "Создать",
|
||||
"creating": "Создание…",
|
||||
"save": "Сохранить",
|
||||
"saving": "Сохранение…",
|
||||
"cancel": "Отмена",
|
||||
"reimbursement": "Возмещение"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Файл слишком большой",
|
||||
"description": "Максимальный размер файла, который можно загрузить — {maxSize}. Размер вашего файла — {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Ошибка при загрузке документа",
|
||||
"description": "При загрузке документа что-то пошло не так. Пожалуйста, повторите позднее или попробуйте загрузить другой файл.",
|
||||
"retry": "Повторить"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Создать расход из чека",
|
||||
"title": "Создать из чека",
|
||||
"description": "Извлечение информации о расходах из фотографии чека",
|
||||
"body": "Загрузите фотографию чека, и мы попытаемся отсканировать его, чтобы извлечь информацию о расходах.",
|
||||
"selectImage": "Выбрать изображение…",
|
||||
"titleLabel": "Название:",
|
||||
"categoryLabel": "Категория:",
|
||||
"amountLabel": "Сумма:",
|
||||
"dateLabel": "Дата:",
|
||||
"editNext": "Вы сможете изменить эту информацию позднее.",
|
||||
"continue": "Продолжить"
|
||||
},
|
||||
"unknown": "Неизвестно",
|
||||
"TooBigToast": {
|
||||
"title": "Файл слишком большой",
|
||||
"description": "Максимальный размер файла, который можно загрузить — {maxSize}. Размер вашего файла — {size}."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Ошибка при загрузке документа",
|
||||
"description": "При загрузке документа что-то пошло не так. Пожалуйста, повторите позднее или попробуйте загрузить другой файл.",
|
||||
"retry": "Повторить"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Балансы",
|
||||
"description": "Это список балансов всех участников группы. Баланс увеличивается у тех, кто оплачивает расход, и уменьшается у тех, между кем он был распределен.",
|
||||
"Reimbursements": {
|
||||
"title": "Предложенные возмещения",
|
||||
"description": "Вот список задолженностей между участниками.",
|
||||
"noImbursements": "Похоже, все в расчете 😁",
|
||||
"owes": "<strong>{from}</strong> должен <strong>{to}</strong>",
|
||||
"markAsPaid": "Пометить оплаченным"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Статистика",
|
||||
"Totals": {
|
||||
"title": "Итоговые суммы",
|
||||
"description": "Общая информация о расходах вашей группы.",
|
||||
"groupSpendings": "Всего потрачено группой",
|
||||
"groupEarnings": "Всего заработано группой",
|
||||
"yourSpendings": "Всего потрачено вами",
|
||||
"yourEarnings": "Всего заработано вами",
|
||||
"yourShare": "Ваша суммарная доля"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Активность",
|
||||
"description": "Обзор действий, совершенных участниками этой группы.",
|
||||
"noActivity": "История действий пуста.",
|
||||
"someone": "Аноним",
|
||||
"settingsModified": "Настройки группы изменены участником <strong>{participant}</strong>.",
|
||||
"expenseCreated": "Расход <em>{expense}</em> создан участником <strong>{participant}</strong>.",
|
||||
"expenseUpdated": "Расход <em>{expense}</em> изменен участником <strong>{participant}</strong>.",
|
||||
"expenseDeleted": "Расход <em>{expense}</em> удален участником <strong>{participant}</strong>.",
|
||||
"Groups": {
|
||||
"today": "Сегодня",
|
||||
"yesterday": "Вчера",
|
||||
"earlierThisWeek": "Ранее на этой неделе",
|
||||
"lastWeek": "На прошлой неделе",
|
||||
"earlierThisMonth": "Ранее в этом месяце",
|
||||
"lastMonth": "В прошлом месяце",
|
||||
"earlierThisYear": "Ранее в этом году",
|
||||
"lastYear": "В прошлом году",
|
||||
"older": "Очень давно"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Информация",
|
||||
"description": "В этом разделе вы можете добавить важную для участников информацию.",
|
||||
"empty": "Информации нет."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Настройки"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Поделиться",
|
||||
"description": "Чтобы другие участники получили доступ к этой группе и смогли добавлять расходы, отправьте им этот URL.",
|
||||
"warning": "Внимание!",
|
||||
"warningHelp": "Любой человек с доступом к этой ссылке сможет просматривать и редактировать расходы. Будьте осторожны!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Введите как минимум один символ.",
|
||||
"min2": "Введите как минимум два символа.",
|
||||
"max5": "Введите максимум 5 символов.",
|
||||
"max50": "Введите максимум 50 символов.",
|
||||
"duplicateParticipantName": "Участник с таким именем уже существует.",
|
||||
"titleRequired": "Пожалуйста, введите название.",
|
||||
"invalidNumber": "Неверное число.",
|
||||
"amountRequired": "Пожалуйста, введите сумму.",
|
||||
"amountNotZero": "Сумма не может быть нулевой.",
|
||||
"amountTenMillion": "Сумма должна быть меньше 10 000 000.",
|
||||
"paidByRequired": "Пожалуйста, выберите участника.",
|
||||
"paidForMin1": "За этот расход должен заплатить как минимум один участник.",
|
||||
"noZeroShares": "Все доли должны быть больше 0.",
|
||||
"amountSum": "Сумма расхода должна быть равна сумме значений, распределенных между участниками.",
|
||||
"percentageSum": "Сумма процентов должна быть равна 100."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Поиск категорий...",
|
||||
"noCategory": "Категорий не нашлось.",
|
||||
"Uncategorized": {
|
||||
"heading": "Без категории",
|
||||
"General": "Общее",
|
||||
"Payment": "Выплата"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Развлечения",
|
||||
"Entertainment": "Развлечения",
|
||||
"Games": "Игры",
|
||||
"Movies": "Кино",
|
||||
"Music": "Музыка",
|
||||
"Sports": "Спорт"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Еда и напитки",
|
||||
"Food and Drink": "Еда и напитки",
|
||||
"Dining Out": "Рестораны и кафе",
|
||||
"Groceries": "Продукты",
|
||||
"Liquor": "Напитки"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Дом",
|
||||
"Home": "Дом",
|
||||
"Electronics": "Электроника",
|
||||
"Furniture": "Мебель",
|
||||
"Household Supplies": "Расходные материалы",
|
||||
"Maintenance": "Уборка",
|
||||
"Mortgage": "Ипотека",
|
||||
"Pets": "Домашние животные",
|
||||
"Rent": "Аренда",
|
||||
"Services": "Коммунальные расходы"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Жизнь",
|
||||
"Childcare": "Дети",
|
||||
"Clothing": "Одежда",
|
||||
"Education": "Образование",
|
||||
"Gifts": "Подарки",
|
||||
"Insurance": "Страховки",
|
||||
"Medical Expenses": "Медицина",
|
||||
"Taxes": "Налоги"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Транспорт",
|
||||
"Transportation": "Транспорт",
|
||||
"Bicycle": "Велосипед",
|
||||
"Bus/Train": "Автобусы и поезда",
|
||||
"Car": "Авто",
|
||||
"Gas/Fuel": "Топливо",
|
||||
"Hotel": "Отели",
|
||||
"Parking": "Парковка",
|
||||
"Plane": "Самолеты",
|
||||
"Taxi": "Такси"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Коммунальные расходы",
|
||||
"Utilities": "Коммунальные расходы",
|
||||
"Cleaning": "Клининг",
|
||||
"Electricity": "Электричество",
|
||||
"Heat/Gas": "Отопление/Газ",
|
||||
"Trash": "Утилизация отходов",
|
||||
"TV/Phone/Internet": "ТВ/Телефон/Интернет",
|
||||
"Water": "Вода"
|
||||
}
|
||||
}
|
||||
}
|
||||
389
messages/tr-TR.json
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "<strong>Masrafları</strong> <strong>Arkadaşlar ve Aile</strong> ile paylaş",
|
||||
"description": "Yeni <strong>Spliit</strong> kurulumunuza hoş geldiniz !",
|
||||
"button": {
|
||||
"groups": "Gruplara git",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Gruplar"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Montréal, Québec 🇨🇦'da yapıldı",
|
||||
"builtBy": "<author>Sebastien Castiel</author> ve <source>katkıda bulunanlar</source> tarafından geliştirildi"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Masraflar",
|
||||
"description": "Grubunuz için oluşturduğunuz masraflar burada.",
|
||||
"create": "Masraf oluştur",
|
||||
"createFirst": "İlk masrafı oluştur",
|
||||
"noExpenses": "Grubunuzda henüz herhangi bir masraf yok.",
|
||||
"exportJson": "JSON olarak dışa aktar",
|
||||
"exportCsv": "CSV olarak dışa aktar",
|
||||
"searchPlaceholder": "Bir masraf arayın…",
|
||||
"ActiveUserModal": {
|
||||
"title": "Kimsiniz?",
|
||||
"description": "Bilgilerin nasıl görüntüleneceğini özelleştirebilmemiz için hangi katılımcı olduğunuzu belirtin.",
|
||||
"nobody": "Kimseyi seçmek istemiyorum",
|
||||
"save": "Değişiklikleri kaydet",
|
||||
"footer": "Bu ayar daha sonra grup ayarlarında değiştirilebilir."
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Yaklaşan",
|
||||
"thisWeek": "Bu hafta",
|
||||
"earlierThisMonth": "Bu ayın başlarında",
|
||||
"lastMonth": "Geçen ay",
|
||||
"earlierThisYear": "Bu yılın başlarında",
|
||||
"lastYear": "Geçen yıl",
|
||||
"older": "Daha eski"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong> tarafından ödendi, <paidFor></paidFor> için",
|
||||
"receivedBy": "<strong>{paidBy}</strong> tarafından alındı, <paidFor></paidFor> için",
|
||||
"yourBalance": "Bakiyeniz:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Gruplarım",
|
||||
"create": "Oluştur",
|
||||
"loadingRecent": "Son gruplar yükleniyor…",
|
||||
"NoRecent": {
|
||||
"description": "Son zamanlarda hiç grup ziyaret etmediniz.",
|
||||
"create": "Bir tane oluştur",
|
||||
"orAsk": "ya da bir arkadaşınızdan mevcut bir grubun bağlantısını göndermesini isteyin."
|
||||
},
|
||||
"recent": "Son gruplar",
|
||||
"starred": "Yıldızlı gruplar",
|
||||
"archived": "Arşivlenmiş gruplar",
|
||||
"archive": "Grubu arşivle",
|
||||
"unarchive": "Arşivden çıkar",
|
||||
"removeRecent": "Son gruplardan kaldır",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Grup kaldırıldı",
|
||||
"description": "Grup son gruplar listenizden kaldırıldı.",
|
||||
"undoAlt": "Grup kaldırma işlemini geri al",
|
||||
"undo": "Geri al"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "URL ile ekle",
|
||||
"title": "URL ile grup ekle",
|
||||
"description": "Bir grup sizinle paylaşıldıysa, URL'sini buraya yapıştırarak listeye ekleyebilirsiniz.",
|
||||
"error": "Üzgünüz, sağladığınız URL'den bir grup bulamadık…"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Bu grup mevcut değil.",
|
||||
"link": "Son ziyaret ettiğiniz gruplara dön"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Grup bilgileri",
|
||||
"NameField": {
|
||||
"label": "Grup adı",
|
||||
"placeholder": "Yaz tatili",
|
||||
"description": "Grubunuz için bir ad girin."
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Grup bilgisi",
|
||||
"placeholder": "Grup katılımcıları için hangi bilgiler önemli?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Para birimi simgesi",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "Tutarları göstermek için kullanacağız."
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Katılımcılar",
|
||||
"description": "Her katılımcı için bir ad girin.",
|
||||
"protectedParticipant": "Bu katılımcı masraflara dahil olduğundan kaldırılamaz.",
|
||||
"new": "Yeni",
|
||||
"add": "Katılımcı ekle",
|
||||
"John": "John",
|
||||
"Jane": "Jane",
|
||||
"Jack": "Jack"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Yerel ayarlar",
|
||||
"description": "Bu ayarlar cihaz bazında belirlenir ve deneyiminizi özelleştirmek için kullanılır.",
|
||||
"ActiveUserField": {
|
||||
"label": "Aktif kullanıcı",
|
||||
"placeholder": "Bir katılımcı seçin",
|
||||
"none": "Yok",
|
||||
"description": "Masrafların varsayılan olarak kimin adına ekleneceği."
|
||||
},
|
||||
"save": "Kaydet",
|
||||
"saving": "Kaydediliyor…",
|
||||
"create": "Oluştur",
|
||||
"creating": "Oluşturuluyor…",
|
||||
"cancel": "İptal"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Gelir oluştur",
|
||||
"edit": "Geliri düzenle",
|
||||
"TitleField": {
|
||||
"label": "Gelir başlığı",
|
||||
"placeholder": "Pazartesi akşamı restoran",
|
||||
"description": "Gelir için bir açıklama girin."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Gelir tarihi",
|
||||
"description": "Gelirin alındığı tarihi girin."
|
||||
},
|
||||
"categoryFieldDescription": "Gelir kategorisini seçin.",
|
||||
"paidByField": {
|
||||
"label": "Geliri alan",
|
||||
"description": "Geliri alan katılımcıyı seçin."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Gelirin alındığı kişiler",
|
||||
"description": "Gelirin kim(ler) için alındığını seçin."
|
||||
},
|
||||
"splitModeDescription": "Gelirin nasıl paylaştırılacağını seçin.",
|
||||
"attachDescription": "Gelire makbuz ekleyin ve görüntüleyin."
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Masraf oluştur",
|
||||
"edit": "Masrafı düzenle",
|
||||
"TitleField": {
|
||||
"label": "Masraf başlığı",
|
||||
"placeholder": "Pazartesi akşamı restoran",
|
||||
"description": "Masraf için bir açıklama girin."
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Masraf tarihi",
|
||||
"description": "Masrafın ödendiği tarihi girin."
|
||||
},
|
||||
"categoryFieldDescription": "Masraf kategorisini seçin.",
|
||||
"paidByField": {
|
||||
"label": "Ödeyen",
|
||||
"description": "Masrafı ödeyen katılımcıyı seçin."
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Masraf kimin için ödendi",
|
||||
"description": "Masrafın kim(ler) için ödendiğini seçin."
|
||||
},
|
||||
"splitModeDescription": "Masrafın nasıl paylaştırılacağını seçin.",
|
||||
"attachDescription": "Masrafa makbuz ekleyin ve görüntüleyin."
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Tutar"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Bu bir geri ödeme"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Kategori"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Notlar"
|
||||
},
|
||||
"selectNone": "Hiçbirini seçme",
|
||||
"selectAll": "Hepsini seç",
|
||||
"shares": "pay",
|
||||
"advancedOptions": "Gelişmiş paylaşım seçenekleri…",
|
||||
"SplitModeField": {
|
||||
"label": "Paylaşım modu",
|
||||
"evenly": "Eşit pay",
|
||||
"byShares": "Eşit olmayan – Pay adedine göre",
|
||||
"byPercentage": "Eşit olmayan – Yüzdeye göre",
|
||||
"byAmount": "Eşit olmayan – Tutar bazında",
|
||||
"saveAsDefault": "Varsayılan paylaşım ayarları olarak kaydet"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Sil",
|
||||
"title": "Bu masraf silinsin mi?",
|
||||
"description": "Bu masrafı gerçekten silmek istiyor musunuz? Bu işlem geri alınamaz.",
|
||||
"yes": "Evet",
|
||||
"cancel": "İptal"
|
||||
},
|
||||
"attachDocuments": "Belge ekle",
|
||||
"create": "Oluştur",
|
||||
"creating": "Oluşturuluyor…",
|
||||
"save": "Kaydet",
|
||||
"saving": "Kaydediliyor…",
|
||||
"cancel": "İptal",
|
||||
"reimbursement": "Geri ödeme"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Dosya çok büyük",
|
||||
"description": "Yükleyebileceğiniz maksimum dosya boyutu {maxSize}. Dosyanız {size} boyutunda."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Belge yüklenirken hata oluştu",
|
||||
"description": "Belge yüklenirken bir sorun oluştu. Lütfen daha sonra tekrar deneyin veya farklı bir dosya seçin.",
|
||||
"retry": "Tekrar dene"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Makbuzdan masraf oluştur",
|
||||
"title": "Makbuzdan oluştur",
|
||||
"description": "Bir makbuz fotoğrafındaki masraf bilgilerini çekin.",
|
||||
"body": "Bir makbuz fotoğrafı yükleyin, mümkünse masraf bilgilerini otomatik olarak çıkaracağız.",
|
||||
"selectImage": "Resim seç…",
|
||||
"titleLabel": "Başlık:",
|
||||
"categoryLabel": "Kategori:",
|
||||
"amountLabel": "Tutar:",
|
||||
"dateLabel": "Tarih:",
|
||||
"editNext": "Masraf bilgilerini sonraki adımda düzenleyebileceksiniz.",
|
||||
"continue": "Devam et"
|
||||
},
|
||||
"unknown": "Bilinmiyor",
|
||||
"TooBigToast": {
|
||||
"title": "Dosya çok büyük",
|
||||
"description": "Yükleyebileceğiniz maksimum dosya boyutu {maxSize}. Dosyanız {size} boyutunda."
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Belge yüklenirken hata oluştu",
|
||||
"description": "Belge yüklenirken bir sorun oluştu. Lütfen daha sonra tekrar deneyin veya farklı bir dosya seçin.",
|
||||
"retry": "Tekrar dene"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Bakiyeler",
|
||||
"description": "Her katılımcının ödediği veya kendisi için ödenen tutar burada gösterilir.",
|
||||
"Reimbursements": {
|
||||
"title": "Önerilen geri ödemeler",
|
||||
"description": "Katılımcılar arasındaki en uygun geri ödeme önerileri aşağıdadır.",
|
||||
"noImbursements": "Görünüşe göre grubunuzun hiçbir geri ödemeye ihtiyacı yok 😁",
|
||||
"owes": "<strong>{from}</strong>, <strong>{to}</strong>'ya borçlu",
|
||||
"markAsPaid": "Ödendi olarak işaretle"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "İstatistikler",
|
||||
"Totals": {
|
||||
"title": "Toplamlar",
|
||||
"description": "Grubun tüm harcama özeti.",
|
||||
"groupSpendings": "Grubun toplam harcamaları",
|
||||
"groupEarnings": "Grubun toplam gelirleri",
|
||||
"yourSpendings": "Sizin toplam harcamalarınız",
|
||||
"yourEarnings": "Sizin toplam gelirleriniz",
|
||||
"yourShare": "Sizin toplam payınız"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Etkinlik",
|
||||
"description": "Bu gruptaki tüm etkinliklerin genel görünümü.",
|
||||
"noActivity": "Grubunuzda henüz bir etkinlik yok.",
|
||||
"someone": "Birisi",
|
||||
"settingsModified": "Grup ayarları <strong>{participant}</strong> tarafından değiştirildi.",
|
||||
"expenseCreated": "Masraf <em>{expense}</em>, <strong>{participant}</strong> tarafından oluşturuldu.",
|
||||
"expenseUpdated": "Masraf <em>{expense}</em>, <strong>{participant}</strong> tarafından güncellendi.",
|
||||
"expenseDeleted": "Masraf <em>{expense}</em>, <strong>{participant}</strong> tarafından silindi.",
|
||||
"Groups": {
|
||||
"today": "Bugün",
|
||||
"yesterday": "Dün",
|
||||
"earlierThisWeek": "Bu haftanın başlarında",
|
||||
"lastWeek": "Geçen hafta",
|
||||
"earlierThisMonth": "Bu ayın başlarında",
|
||||
"lastMonth": "Geçen ay",
|
||||
"earlierThisYear": "Bu yılın başlarında",
|
||||
"lastYear": "Geçen yıl",
|
||||
"older": "Daha eski"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Bilgi",
|
||||
"description": "Grup katılımcıları için yararlı olabilecek bilgileri buraya ekleyebilirsiniz.",
|
||||
"empty": "Henüz grup bilgisi bulunmuyor."
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Ayarlar"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Paylaş",
|
||||
"description": "Diğer katılımcıların grubu görmesi ve masraf ekleyebilmesi için onlarla bu grubun URL'sini paylaşın.",
|
||||
"warning": "Uyarı!",
|
||||
"warningHelp": "Grubun URL'sine sahip olan herkes masrafları görebilir ve düzenleyebilir. Lütfen paylaşırken dikkatli olun!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "En az bir karakter girin.",
|
||||
"min2": "En az iki karakter girin.",
|
||||
"max5": "En fazla beş karakter girin.",
|
||||
"max50": "En fazla 50 karakter girin.",
|
||||
"duplicateParticipantName": "Başka bir katılımcı zaten bu ada sahip.",
|
||||
"titleRequired": "Lütfen bir başlık girin.",
|
||||
"invalidNumber": "Geçersiz numara.",
|
||||
"amountRequired": "Bir tutar girmelisiniz.",
|
||||
"amountNotZero": "Tutar sıfır olamaz.",
|
||||
"amountTenMillion": "Tutar 10.000.000'dan düşük olmalı.",
|
||||
"paidByRequired": "Bir katılımcı seçmelisiniz.",
|
||||
"paidForMin1": "Masraf en az bir katılımcı için ödenmiş olmalıdır.",
|
||||
"noZeroShares": "Tüm paylar 0'dan büyük olmalıdır.",
|
||||
"amountSum": "Tutarların toplamı masraf tutarına eşit olmalıdır.",
|
||||
"percentageSum": "Yüzdelerin toplamı 100 olmalıdır."
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Kategori ara...",
|
||||
"noCategory": "Kategori bulunamadı.",
|
||||
"Uncategorized": {
|
||||
"heading": "Kategorize Edilmemiş",
|
||||
"General": "Genel",
|
||||
"Payment": "Ödeme"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Eğlence",
|
||||
"Entertainment": "Eğlence",
|
||||
"Games": "Oyunlar",
|
||||
"Movies": "Filmler",
|
||||
"Music": "Müzik",
|
||||
"Sports": "Spor"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Yiyecek ve İçecek",
|
||||
"Food and Drink": "Yiyecek ve İçecek",
|
||||
"Dining Out": "Dışarıda Yemek",
|
||||
"Groceries": "Market Alışverişi",
|
||||
"Liquor": "Alkollü İçecekler"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Ev",
|
||||
"Home": "Ev",
|
||||
"Electronics": "Elektronik",
|
||||
"Furniture": "Mobilya",
|
||||
"Household Supplies": "Ev İhtiyaçları",
|
||||
"Maintenance": "Bakım",
|
||||
"Mortgage": "Mortgage",
|
||||
"Pets": "Evcil Hayvanlar",
|
||||
"Rent": "Kira",
|
||||
"Services": "Hizmetler"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Yaşam",
|
||||
"Childcare": "Çocuk Bakımı",
|
||||
"Clothing": "Giyim",
|
||||
"Education": "Eğitim",
|
||||
"Gifts": "Hediyeler",
|
||||
"Insurance": "Sigorta",
|
||||
"Medical Expenses": "Sağlık Giderleri",
|
||||
"Taxes": "Vergiler"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Ulaşım",
|
||||
"Transportation": "Ulaşım",
|
||||
"Bicycle": "Bisiklet",
|
||||
"Bus/Train": "Otobüs/Tren",
|
||||
"Car": "Araba",
|
||||
"Gas/Fuel": "Benzin/Yakıt",
|
||||
"Hotel": "Otel",
|
||||
"Parking": "Otopark",
|
||||
"Plane": "Uçak",
|
||||
"Taxi": "Taksi"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Faturalar",
|
||||
"Utilities": "Faturalar",
|
||||
"Cleaning": "Temizlik",
|
||||
"Electricity": "Elektrik",
|
||||
"Heat/Gas": "Isınma/Gaz",
|
||||
"Trash": "Çöp",
|
||||
"TV/Phone/Internet": "TV/Telefon/İnternet",
|
||||
"Water": "Su"
|
||||
}
|
||||
}
|
||||
}
|
||||
389
messages/uk-UA.json
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "Ділися <strong>витратами</strong> з <strong>друзями та родиною</strong>",
|
||||
"description": "Ласкаво просимо у ваш новий <strong>Spliit</strong>!",
|
||||
"button": {
|
||||
"groups": "Перейти до груп",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "Групи"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Зроблено в Монреалі, Квебек 🇨🇦",
|
||||
"builtBy": "Створено <author>Себастіаном Кастіелем</author> та <source>учасниками</source>"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "Витрати",
|
||||
"description": "Тут знаходяться витрати вашої групи",
|
||||
"create": "Створити витрату",
|
||||
"createFirst": "Створіть першу витрату",
|
||||
"noExpenses": "У вашій групі ще немає витрат",
|
||||
"exportJson": "Експортувати у JSON",
|
||||
"exportCsv": "Експортувати у CSV",
|
||||
"searchPlaceholder": "Пошук витрат...",
|
||||
"ActiveUserModal": {
|
||||
"title": "Хто ви?",
|
||||
"description": "Скажіть нам, хто ви серед учасників, щоб ми могли налаштувати відображення інформації під вас",
|
||||
"nobody": "Я не хочу нікого обирати",
|
||||
"save": "Зберегти зміни",
|
||||
"footer": "Це налаштування можна змінити пізніше в налаштуваннях групи"
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "Майбутні",
|
||||
"thisWeek": "Цього тижня",
|
||||
"earlierThisMonth": "Раніше цього місяця",
|
||||
"lastMonth": "Минулого місяця",
|
||||
"earlierThisYear": "Раніше цього року",
|
||||
"lastYear": "Минулого року",
|
||||
"older": "Старіші"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "Сплачено <strong>{paidBy}</strong> за <paidFor></paidFor>",
|
||||
"receivedBy": "Отримано <strong>{paidBy}</strong> за <paidFor></paidFor>",
|
||||
"yourBalance": "Ваш баланс:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "Мої групи",
|
||||
"create": "Створити",
|
||||
"loadingRecent": "Завантаження нещодавніх груп...",
|
||||
"NoRecent": {
|
||||
"description": "Ви не відвідували жодних груп останнім часом",
|
||||
"create": "Створіть групу",
|
||||
"orAsk": "або попросіть друга надіслати вам посилання на існуючу"
|
||||
},
|
||||
"recent": "Нещодавні групи",
|
||||
"starred": "Обрані групи",
|
||||
"archived": "Архівовані групи",
|
||||
"archive": "Архівувати групу",
|
||||
"unarchive": "Розархівувати групу",
|
||||
"removeRecent": "Видалити з останніх груп",
|
||||
"RecentRemovedToast": {
|
||||
"title": "Група була видалена",
|
||||
"description": "Група видалена зі списку ваших нещодавніх груп",
|
||||
"undoAlt": "Скасувати видалення групи",
|
||||
"undo": "Скасувати"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "Додати за URL",
|
||||
"title": "Додати групу за URL",
|
||||
"description": "Якщо з вами поділились групою, ви можете вставити її URL тут, щоб додати до свого списку",
|
||||
"error": "На жаль, ми не змогли знайти групу за наданим URL"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "Цієї групи не існує",
|
||||
"link": "Перейти до нещодавно відвіданих груп"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "Інформація про групу",
|
||||
"NameField": {
|
||||
"label": "Назва групи",
|
||||
"placeholder": "Літні канікули",
|
||||
"description": "Введіть назву для вашої групи"
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "Інформація про групу",
|
||||
"placeholder": "Яка інформація важлива для учасників групи?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "Символ валюти",
|
||||
"placeholder": "₴, $, €, £..",
|
||||
"description": "Ми будемо використовувати його для відображення сум"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "Учасники",
|
||||
"description": "Введіть ім'я кожного учасника",
|
||||
"protectedParticipant": "Цей учасник бере участь у витратах і не може бути видалений",
|
||||
"new": "Новий",
|
||||
"add": "Додати учасника",
|
||||
"John": "Андрій",
|
||||
"Jane": "Оксана",
|
||||
"Jack": "Василь"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Локальні налаштування",
|
||||
"description": "Ці налаштування встановлюються на кожному пристрої окремо і використовуються для налаштування інтерфейсу під вас",
|
||||
"ActiveUserField": {
|
||||
"label": "Активний користувач",
|
||||
"placeholder": "Обрати учасника",
|
||||
"none": "Ніхто",
|
||||
"description": "Користувач використовується за замовчуванням для оплати витрат"
|
||||
},
|
||||
"save": "Зберегти",
|
||||
"saving": "Збереження...",
|
||||
"create": "Створити",
|
||||
"creating": "Створення...",
|
||||
"cancel": "Скасувати"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "Створити дохід",
|
||||
"edit": "Редагувати дохід",
|
||||
"TitleField": {
|
||||
"label": "Назва доходу",
|
||||
"placeholder": "Ресторан в понеділок ввечері",
|
||||
"description": "Введіть опис для доходу"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Дата доходу",
|
||||
"description": "Введіть дату, коли було отримано дохід"
|
||||
},
|
||||
"categoryFieldDescription": "Оберіть категорію доходу",
|
||||
"paidByField": {
|
||||
"label": "Отримав",
|
||||
"description": "Оберіть учасника, який отримав дохід"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Учасники",
|
||||
"description": "Виберіть тих, між ким цей дохід буде розподілено"
|
||||
},
|
||||
"splitModeDescription": "Оберіть, як розділити дохід між учасниками",
|
||||
"attachDescription": "Перегляньте та прикріпіть чеки до доходу"
|
||||
},
|
||||
"Expense": {
|
||||
"create": "Створити витрату",
|
||||
"edit": "Редагувати витрату",
|
||||
"TitleField": {
|
||||
"label": "Назва витрати",
|
||||
"placeholder": "Ресторан в понеділок ввечері",
|
||||
"description": "Введіть опис для витрати"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "Дата витрати",
|
||||
"description": "Введіть дату, коли було сплачено"
|
||||
},
|
||||
"categoryFieldDescription": "Оберіть категорію витрати",
|
||||
"paidByField": {
|
||||
"label": "Сплатив",
|
||||
"description": "Оберіть учасника, який сплатив"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "Учасники",
|
||||
"description": "Оберіть тих, між ким цю витрату буде розподілено. Якщо ця витрата - відшкодування учаснику (учасникам), виберіть тільки його (їх)."
|
||||
},
|
||||
"splitModeDescription": "Оберіть, як розділити витрату",
|
||||
"attachDescription": "Перегляньте та прикріпіть чеки до витрати"
|
||||
},
|
||||
"amountField": {
|
||||
"label": "Сума"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "Це відшкодування"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "Категорія"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "Примітки"
|
||||
},
|
||||
"selectNone": "Обрати жодного",
|
||||
"selectAll": "Обрати всіх",
|
||||
"shares": "частка(и)",
|
||||
"advancedOptions": "Розширені опції поділу..",
|
||||
"SplitModeField": {
|
||||
"label": "Режим поділу",
|
||||
"evenly": "Рівномірно",
|
||||
"byShares": "Нерівномірно – за частками",
|
||||
"byPercentage": "Нерівномірно – за відсотками",
|
||||
"byAmount": "Нерівномірно – за сумами",
|
||||
"saveAsDefault": "Зберегти як параметри поділу за замовчуванням"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "Видалити",
|
||||
"title": "Видалити цю витрату?",
|
||||
"description": "Ви дійсно хочете видалити цю витрату? Ця дія не може бути скасована",
|
||||
"yes": "Так",
|
||||
"cancel": "Скасувати"
|
||||
},
|
||||
"attachDocuments": "Прикріпити документи",
|
||||
"create": "Створити",
|
||||
"creating": "Створення..",
|
||||
"save": "Зберегти",
|
||||
"saving": "Збереження..",
|
||||
"cancel": "Скасувати",
|
||||
"reimbursement": "Відшкодування"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "Файл занадто великий",
|
||||
"description": "Максимальний розмір файлу, який можна завантажити, становить {maxSize}. Ваш файл {size}"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Помилка під час завантаження документа",
|
||||
"description": "Виникла помилка під час завантаження документа. Будь ласка, спробуйте ще раз пізніше або виберіть інший файл",
|
||||
"retry": "Спробувати ще раз"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "Створити витрату з чека",
|
||||
"title": "Створити з чека",
|
||||
"description": "Отримайте інформацію про витрати з фото чека",
|
||||
"body": "Завантажте фото чека, і ми спробуємо витягнути інформацію про витрати, якщо це можливо",
|
||||
"selectImage": "Вибрати зображення..",
|
||||
"titleLabel": "Назва:",
|
||||
"categoryLabel": "Категорія:",
|
||||
"amountLabel": "Сума:",
|
||||
"dateLabel": "Дата:",
|
||||
"editNext": "Ви зможете відредагувати інформацію про витрати пізніше",
|
||||
"continue": "Продовжити"
|
||||
},
|
||||
"unknown": "Невідомо",
|
||||
"TooBigToast": {
|
||||
"title": "Файл занадто великий",
|
||||
"description": "Максимальний розмір файлу, який можна завантажити, становить {maxSize}. Ваш файл {size}"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "Помилка під час завантаження документа",
|
||||
"description": "Виникла помилка під час завантаження документа. Будь ласка, спробуйте ще раз пізніше або виберіть інший файл",
|
||||
"retry": "Спробувати ще раз"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "Баланси",
|
||||
"description": "Це список балансів всіх учасників групи. Баланс збільшується у тих, хто слачує витрату, і зменшується в тих, між ким вона була розподілена",
|
||||
"Reimbursements": {
|
||||
"title": "Запропоновані відшкодування",
|
||||
"description": "Ось пропозиції для оптимізованих відшкодувань між учасниками",
|
||||
"noImbursements": "Схоже, ніхто нікому не винен 😁",
|
||||
"owes": "<strong>{from}</strong> винен <strong>{to}</strong>",
|
||||
"markAsPaid": "Позначити як сплачене"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "Статистика",
|
||||
"Totals": {
|
||||
"title": "Загальні дані",
|
||||
"description": "Загальний огляд витрат групи",
|
||||
"groupSpendings": "Загальні витрати групи",
|
||||
"groupEarnings": "Загальні доходи групи",
|
||||
"yourSpendings": "Ваші загальні витрати",
|
||||
"yourEarnings": "Ваші загальні доходи",
|
||||
"yourShare": "Ваша частка"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "Активність",
|
||||
"description": "Огляд усієї активності в цій групі",
|
||||
"noActivity": "У вашій групі ще немає активності",
|
||||
"someone": "Хтось",
|
||||
"settingsModified": "Налаштування групи змінені <strong>{participant}</strong>",
|
||||
"expenseCreated": "Витрата <em>{expense}</em> створена <strong>{participant}</strong>",
|
||||
"expenseUpdated": "Витрата <em>{expense}</em> оновлена <strong>{participant}</strong>",
|
||||
"expenseDeleted": "Витрата <em>{expense}</em> видалена <strong>{participant}</strong>",
|
||||
"Groups": {
|
||||
"today": "Сьогодні",
|
||||
"yesterday": "Вчора",
|
||||
"earlierThisWeek": "Раніше цього тижня",
|
||||
"lastWeek": "Минулого тижня",
|
||||
"earlierThisMonth": "Раніше цього місяця",
|
||||
"lastMonth": "Минулого місяця",
|
||||
"earlierThisYear": "Раніше цього року",
|
||||
"lastYear": "Минулого року",
|
||||
"older": "Старіші"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "Інформація",
|
||||
"description": "Використовуйте це місце, щоб додати будь-яку інформацію, яка може бути корисною для учасників групи",
|
||||
"empty": "Ще немає інформації про групу"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Налаштування"
|
||||
},
|
||||
"Share": {
|
||||
"title": "Поділитися",
|
||||
"description": "Щоб інші учасники могли побачити групу і додати витрати, поділіться з ними її URL",
|
||||
"warning": "Попередження!",
|
||||
"warningHelp": "Кожна людина з URL групи зможе переглядати та редагувати витрати. Діліться з обережністю!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "Введіть принаймні один символ",
|
||||
"min2": "Введіть принаймні два символи",
|
||||
"max5": "Введіть не більше п'яти символів",
|
||||
"max50": "Введіть не більше 50 символів",
|
||||
"duplicateParticipantName": "Інший учасник уже має це ім'я",
|
||||
"titleRequired": "Будь ласка, введіть назву",
|
||||
"invalidNumber": "Невірний номер",
|
||||
"amountRequired": "Необхідно ввести суму",
|
||||
"amountNotZero": "Сума не повинна дорівнювати нулю",
|
||||
"amountTenMillion": "Сума повинна бути меншою за 10,000,000",
|
||||
"paidByRequired": "Ви повинні обрати учасника",
|
||||
"paidForMin1": "Витрата повинна бути сплачена принаймні для одного учасника",
|
||||
"noZeroShares": "Усі частки повинні бути більшими за 0",
|
||||
"amountSum": "Сума повинна відповідати витраті",
|
||||
"percentageSum": "Сума відсотків повинна дорівнювати 100"
|
||||
},
|
||||
"Categories": {
|
||||
"search": "Шукати категорію..",
|
||||
"noCategory": "Категорії не знайдено",
|
||||
"Uncategorized": {
|
||||
"heading": "Без категорії",
|
||||
"General": "Загальне",
|
||||
"Payment": "Оплата"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "Розваги",
|
||||
"Entertainment": "Розваги",
|
||||
"Games": "Ігри",
|
||||
"Movies": "Фільми",
|
||||
"Music": "Музика",
|
||||
"Sports": "Спорт"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "Їжа та напої",
|
||||
"Food and Drink": "Їжа та напої",
|
||||
"Dining Out": "Ресторани",
|
||||
"Groceries": "Продукти",
|
||||
"Liquor": "Алкоголь"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "Дім",
|
||||
"Home": "Дім",
|
||||
"Electronics": "Електроніка",
|
||||
"Furniture": "Меблі",
|
||||
"Household Supplies": "Домашні потреби",
|
||||
"Maintenance": "Обслуговування",
|
||||
"Mortgage": "Іпотека",
|
||||
"Pets": "Домашні тварини",
|
||||
"Rent": "Оренда",
|
||||
"Services": "Послуги"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "Життя",
|
||||
"Childcare": "Догляд за дітьми",
|
||||
"Clothing": "Одяг",
|
||||
"Education": "Освіта",
|
||||
"Gifts": "Подарунки",
|
||||
"Insurance": "Страхування",
|
||||
"Medical Expenses": "Медичні витрати",
|
||||
"Taxes": "Податки"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "Транспорт",
|
||||
"Transportation": "Транспорт",
|
||||
"Bicycle": "Велосипед",
|
||||
"Bus/Train": "Автобус/Поїзд",
|
||||
"Car": "Автомобіль",
|
||||
"Gas/Fuel": "Паливо",
|
||||
"Hotel": "Готель",
|
||||
"Parking": "Паркінг",
|
||||
"Plane": "Літак",
|
||||
"Taxi": "Таксі"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "Комунальні послуги",
|
||||
"Utilities": "Комунальні послуги",
|
||||
"Cleaning": "Прибирання",
|
||||
"Electricity": "Електроенергія",
|
||||
"Heat/Gas": "Опалення/Газ",
|
||||
"Trash": "Сміття",
|
||||
"TV/Phone/Internet": "ТБ/Телефон/Інтернет",
|
||||
"Water": "Вода"
|
||||
}
|
||||
}
|
||||
}
|
||||
443
messages/zh-CN.json
Normal file
@@ -0,0 +1,443 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "与<strong>朋友和家人</strong>共享<strong>开支</strong>",
|
||||
"description": "欢迎使用你的全新<strong>Spliit</strong>实例!",
|
||||
"button": {
|
||||
"groups": "前往群组",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "群组"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "Made in Montréal, Québec 🇨🇦",
|
||||
"builtBy": "由 <author>Sebastien Castiel</author> 以及 <source>社区贡献者们</source> 共同构建"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "消费",
|
||||
"description": "这里有你为你的群组创建的消费。",
|
||||
"create": "创建消费",
|
||||
"createFirst": "创建首个消费",
|
||||
"noExpenses": "你的群组内目前没有任何消费。",
|
||||
"exportJson": "导出到JSON",
|
||||
"exportCsv": "导出到CSV",
|
||||
"searchPlaceholder": "查找消费……",
|
||||
"ActiveUserModal": {
|
||||
"title": "你是哪位?",
|
||||
"description": "告诉我们你在群组中的身份,以便定制你的信息呈现方式。",
|
||||
"nobody": "我不想选择任何人",
|
||||
"save": "保存变更",
|
||||
"footer": "此项设定之后可以在群组设定中修改。"
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "即将到来",
|
||||
"thisWeek": "本周",
|
||||
"earlierThisMonth": "本月早些时候",
|
||||
"lastMonth": "上个月",
|
||||
"earlierThisYear": "本年早些时候",
|
||||
"lastYear": "去年",
|
||||
"older": "更早"
|
||||
},
|
||||
"export": "导出"
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "<strong>{paidBy}</strong> 为 <paidFor></paidFor> 支付。",
|
||||
"receivedBy": "<strong>{paidBy}</strong> 为 <paidFor></paidFor> 接收。",
|
||||
"yourBalance": "你的余额:",
|
||||
"everyone": "所有人",
|
||||
"notInvolved": "您无需支付"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "我的群组",
|
||||
"create": "创建",
|
||||
"loadingRecent": "加载最近的群组……",
|
||||
"NoRecent": {
|
||||
"description": "你最近没有访问任何群组。",
|
||||
"create": "创建一个群组",
|
||||
"orAsk": "或者让你的朋友发给你现有群组的链接。"
|
||||
},
|
||||
"recent": "最近的群组",
|
||||
"starred": "已收藏的群组",
|
||||
"archived": "已归档的群组",
|
||||
"archive": "归档群组",
|
||||
"unarchive": "取消归档群组",
|
||||
"removeRecent": "从最近的群组中删除",
|
||||
"RecentRemovedToast": {
|
||||
"title": "群组已删除",
|
||||
"description": "群组已从你的最近群组列表中删除。",
|
||||
"undoAlt": "撤销群组删除",
|
||||
"undo": "撤销"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "使用URL添加",
|
||||
"title": "使用URL添加一个群组",
|
||||
"description": "如果你被分享了一个群组,你可以将群组的URL粘贴到这里以添加这个群组。",
|
||||
"error": "哎呀,我们没办法根据你的URL找到相应的群组……"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "该群组不存在。",
|
||||
"link": "跳转到最近访问过的群组"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "群组信息",
|
||||
"NameField": {
|
||||
"label": "群组名",
|
||||
"placeholder": "暑假",
|
||||
"description": "为你的群组输入一个名字。"
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "群组信息",
|
||||
"placeholder": "哪些信息与群组成员有关?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "货币标志",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "我们根据这个显示相应的货币金额。"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "群组成员",
|
||||
"description": "输入每位成员的名字。",
|
||||
"protectedParticipant": "群组成员是消费的一部分,不可删除。",
|
||||
"new": "新建",
|
||||
"add": "添加群组成员",
|
||||
"John": "John",
|
||||
"Jane": "Jane",
|
||||
"Jack": "Jack"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "本地设定",
|
||||
"description": "这些设定是按设备设定的,用于定制你的使用体验。",
|
||||
"ActiveUserField": {
|
||||
"label": "当前用户",
|
||||
"placeholder": "选择一个群组成员",
|
||||
"none": "无",
|
||||
"description": "用于支付消费的默认用户。"
|
||||
},
|
||||
"save": "保存",
|
||||
"saving": "保存中……",
|
||||
"create": "创建",
|
||||
"creating": "创建中",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"CurrencyCodeField": {
|
||||
"label": "首选货币",
|
||||
"createDescription": "所有的交易将使用此币种。",
|
||||
"customOption": "自定义"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "创建收入",
|
||||
"edit": "编辑收入",
|
||||
"TitleField": {
|
||||
"label": "收入标题",
|
||||
"placeholder": "周一晚上的餐厅",
|
||||
"description": "描述这个收入。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "收入日期",
|
||||
"description": "输入收到这笔收入的日期。"
|
||||
},
|
||||
"categoryFieldDescription": "选择收入类别。",
|
||||
"paidByField": {
|
||||
"label": "接收到",
|
||||
"description": "选择接收到这笔收入的群组成员。"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "接收给",
|
||||
"description": "选择收入是为谁而收。"
|
||||
},
|
||||
"splitModeDescription": "选择如何划分这笔收入。",
|
||||
"attachDescription": "查看并为这笔收入附加收据。",
|
||||
"currencyField": {
|
||||
"label": "收入币种"
|
||||
}
|
||||
},
|
||||
"Expense": {
|
||||
"create": "创建消费",
|
||||
"edit": "编辑消费",
|
||||
"TitleField": {
|
||||
"label": "消费标题",
|
||||
"placeholder": "周一晚上的餐厅",
|
||||
"description": "描述这个消费。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "消费日期",
|
||||
"description": "输入支付这笔消费的日期。"
|
||||
},
|
||||
"categoryFieldDescription": "选择消费类别。",
|
||||
"paidByField": {
|
||||
"label": "支付自",
|
||||
"description": "选择支付这笔消费的群组成员。",
|
||||
"placeholder": "选择一个参与人"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "支付给",
|
||||
"description": "选择消费是为谁而支出。"
|
||||
},
|
||||
"splitModeDescription": "选择如何划分这笔消费。",
|
||||
"attachDescription": "查看并为这笔消费附加收据。",
|
||||
"currencyField": {
|
||||
"label": "支出币种"
|
||||
},
|
||||
"recurrenceRule": {
|
||||
"label": "订阅式支出",
|
||||
"description": "请选择这笔开销发生的频率。",
|
||||
"none": "无",
|
||||
"daily": "每天",
|
||||
"weekly": "每周",
|
||||
"monthly": "每月"
|
||||
}
|
||||
},
|
||||
"amountField": {
|
||||
"label": "金额"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "这是一笔报销款"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "类别"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "附注"
|
||||
},
|
||||
"selectNone": "取消选中",
|
||||
"selectAll": "全选",
|
||||
"shares": "份额",
|
||||
"advancedOptions": "高级分账选项……",
|
||||
"SplitModeField": {
|
||||
"label": "分账模式",
|
||||
"evenly": "平均分配",
|
||||
"byShares": "按份额分配",
|
||||
"byPercentage": "按百分比分配",
|
||||
"byAmount": "按金额分配",
|
||||
"saveAsDefault": "保存为默认分账设置"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "删除",
|
||||
"title": "要删除这项消费吗?",
|
||||
"description": "你真的确定要删除这项消费吗?此行动不可撤销。",
|
||||
"yes": "确定",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"attachDocuments": "附加文档",
|
||||
"create": "创建",
|
||||
"creating": "创建中……",
|
||||
"save": "保存",
|
||||
"saving": "保存中……",
|
||||
"cancel": "取消",
|
||||
"reimbursement": "报销",
|
||||
"originalAmountField": {
|
||||
"label": "需要转换的金额"
|
||||
},
|
||||
"conversionRateField": {
|
||||
"useApi": "使用Frankfurter提供的汇率",
|
||||
"useCustom": "使用自定义汇率",
|
||||
"label": "汇率"
|
||||
},
|
||||
"conversionRateState": {
|
||||
"error": "抱歉,我们无法获取最新的汇率信息。",
|
||||
"noRate": "请在下方输入自定义汇率。",
|
||||
"currencyNotFound": "抱歉,Frankfurter无法为此货币提供此日期的汇率。",
|
||||
"noDate": "请输入交易发生日期来获取当天的汇率。",
|
||||
"refresh": "刷新",
|
||||
"customRate": "使用自定义汇率"
|
||||
}
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "文件过大",
|
||||
"description": "可上传的最大文件大小为{maxSize},你的文件为{size}。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "上传文档时发生错误",
|
||||
"description": "上传文档时发生了一些错误。请稍后重试或更换文件。",
|
||||
"retry": "重试"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "从收据中创建消费",
|
||||
"title": "从收据中创建",
|
||||
"description": "从收据照片上提取消费信息。",
|
||||
"body": "上传收据的图片,我们会尽可能地从中扫描出消费信息。",
|
||||
"selectImage": "选择图片……",
|
||||
"titleLabel": "标题:",
|
||||
"categoryLabel": "类别:",
|
||||
"amountLabel": "金额:",
|
||||
"dateLabel": "日期:",
|
||||
"editNext": "你之后可以修改消费的信息。",
|
||||
"continue": "继续"
|
||||
},
|
||||
"unknown": "未知",
|
||||
"TooBigToast": {
|
||||
"title": "文件过大",
|
||||
"description": "可上传的最大文件大小为{maxSize},你的文件为{size}。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "上传文档时发生错误",
|
||||
"description": "上传文档时发生了一些错误。请稍后重试或更换文件。",
|
||||
"retry": "重试"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "余额",
|
||||
"description": "这是每位群组成员支付或被支付的金额。",
|
||||
"Reimbursements": {
|
||||
"title": "建议报销",
|
||||
"description": "这里是优化群组成员之间报销的建议。",
|
||||
"noImbursements": "看起来你的群组不需要任何报销😁",
|
||||
"owes": "<strong>{from}</strong> 欠 <strong>{to}</strong>",
|
||||
"markAsPaid": "标记为已支付"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "统计",
|
||||
"Totals": {
|
||||
"title": "总计",
|
||||
"description": "整个群组的花费合计。",
|
||||
"groupSpendings": "群组总计开销",
|
||||
"groupEarnings": "群组总计收入",
|
||||
"yourSpendings": "你的总计开销",
|
||||
"yourEarnings": "你的总计收入",
|
||||
"yourShare": "你的总计份额"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "活动",
|
||||
"description": "该群组所有活动总览",
|
||||
"noActivity": "你的群组目前没有任何活动。",
|
||||
"someone": "某人",
|
||||
"settingsModified": "群组设定已被<strong>{participant}</strong>更改。",
|
||||
"expenseCreated": "消费 <em>{expense}</em> 由 <strong>{participant}</strong> 创建。",
|
||||
"expenseUpdated": "消费 <em>{expense}</em> 由 <strong>{participant}</strong> 更新。",
|
||||
"expenseDeleted": "消费 <em>{expense}</em> 由 <strong>{participant}</strong> 删除。",
|
||||
"Groups": {
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"earlierThisWeek": "这周早些时候",
|
||||
"lastWeek": "上周",
|
||||
"earlierThisMonth": "这月早些时候",
|
||||
"lastMonth": "上月",
|
||||
"earlierThisYear": "这年早些时候",
|
||||
"lastYear": "上年",
|
||||
"older": "更早"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "信息",
|
||||
"description": "使用此处以添加与群组成员相关的任何信息。",
|
||||
"empty": "当前没有群组信息。"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "设定"
|
||||
},
|
||||
"Share": {
|
||||
"title": "分享",
|
||||
"description": "请将此URL分享给其他群组成员,以使其可以查看群组并添加消费。",
|
||||
"warning": "警告!",
|
||||
"warningHelp": "任何持有群组URL的个体都有能够查看并编辑消费。请谨慎分享!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "输入至少1个字符。",
|
||||
"min2": "输入至少2个字符。",
|
||||
"max5": "输入至少5个字符。",
|
||||
"max50": "输入至少50个字符。",
|
||||
"duplicateParticipantName": "此名字已被另一位群组成员占用。",
|
||||
"titleRequired": "请输入标题。",
|
||||
"invalidNumber": "无效数值。",
|
||||
"amountRequired": "你必须输入一个金额。",
|
||||
"amountNotZero": "金额不可以为0。",
|
||||
"amountTenMillion": "金额必须小于10,000,000。",
|
||||
"paidByRequired": "你必须选择一个群组成员。",
|
||||
"paidForMin1": "这项消费必须支付给至少1名群组成员。",
|
||||
"noZeroShares": "所有份额必须大于0。",
|
||||
"amountSum": "金额之和必须等于消费的金额。",
|
||||
"percentageSum": "百分比之和必须等于100。",
|
||||
"ratePositive": "汇率必须为正数(大于0)。"
|
||||
},
|
||||
"Categories": {
|
||||
"search": "搜寻类别……",
|
||||
"noCategory": "未找到类别。",
|
||||
"Uncategorized": {
|
||||
"heading": "未分类",
|
||||
"General": "一般",
|
||||
"Payment": "支付"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "娱乐",
|
||||
"Entertainment": "娱乐",
|
||||
"Games": "游戏",
|
||||
"Movies": "电影",
|
||||
"Music": "音乐",
|
||||
"Sports": "运动"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "饮食",
|
||||
"Food and Drink": "饮食",
|
||||
"Dining Out": "下馆子",
|
||||
"Groceries": "便利店",
|
||||
"Liquor": "酒水"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "居家",
|
||||
"Home": "居家",
|
||||
"Electronics": "电费",
|
||||
"Furniture": "家具",
|
||||
"Household Supplies": "家庭日用品",
|
||||
"Maintenance": "维护",
|
||||
"Mortgage": "贷款",
|
||||
"Pets": "宠物",
|
||||
"Rent": "租金",
|
||||
"Services": "服务"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "生活",
|
||||
"Childcare": "儿童保育",
|
||||
"Clothing": "衣物",
|
||||
"Education": "教育",
|
||||
"Gifts": "礼物",
|
||||
"Insurance": "保险",
|
||||
"Medical Expenses": "医疗支出",
|
||||
"Taxes": "税",
|
||||
"Donation": "捐赠"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "交通",
|
||||
"Transportation": "交通",
|
||||
"Bicycle": "自行车",
|
||||
"Bus/Train": "巴士/列车",
|
||||
"Car": "汽车",
|
||||
"Gas/Fuel": "燃料",
|
||||
"Hotel": "旅馆",
|
||||
"Parking": "停车",
|
||||
"Plane": "飞机",
|
||||
"Taxi": "出租车"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "日常账单",
|
||||
"Utilities": "日常账单",
|
||||
"Cleaning": "清洁费",
|
||||
"Electricity": "电费",
|
||||
"Heat/Gas": "暖气/瓦斯",
|
||||
"Trash": "垃圾",
|
||||
"TV/Phone/Internet": "电视/手机/互联网",
|
||||
"Water": "水"
|
||||
}
|
||||
},
|
||||
"Currencies": {
|
||||
"search": "搜索币种...",
|
||||
"noCurrency": "无法找到此货币。",
|
||||
"custom": {
|
||||
"heading": "自定义"
|
||||
},
|
||||
"common": {
|
||||
"heading": "最常用"
|
||||
},
|
||||
"other": {
|
||||
"heading": "其他币种"
|
||||
}
|
||||
}
|
||||
}
|
||||
389
messages/zh-TW.json
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"Homepage": {
|
||||
"title": "跟<strong>朋友和家人</strong>一起共享<strong>消費紀錄</strong>",
|
||||
"description": "歡迎開始全新的<strong>Spliit</strong>",
|
||||
"button": {
|
||||
"groups": "前往群組",
|
||||
"github": "GitHub"
|
||||
}
|
||||
},
|
||||
"Header": {
|
||||
"groups": "群組"
|
||||
},
|
||||
"Footer": {
|
||||
"madeIn": "來自 🇨🇦 加拿大魁北克蒙特婁",
|
||||
"builtBy": "由 <author>Sebastien Castiel</author> 以及 <source>社群貢獻者</source> 共同創建維護"
|
||||
},
|
||||
"Expenses": {
|
||||
"title": "消費",
|
||||
"description": "這裡是您為群組建立的消費。",
|
||||
"create": "新增消費紀錄",
|
||||
"createFirst": "新增第一筆消費紀錄",
|
||||
"noExpenses": "你的群組內目前沒有任何消費紀錄。",
|
||||
"exportJson": "匯出為 JSON",
|
||||
"exportCsv": "匯出為 CSV",
|
||||
"searchPlaceholder": "搜尋消費紀錄……",
|
||||
"ActiveUserModal": {
|
||||
"title": "你是誰?",
|
||||
"description": "告訴我們您在群組中的身份,以調整我們顯示資訊的方式。",
|
||||
"nobody": "我不想選擇任何人",
|
||||
"save": "儲存更改",
|
||||
"footer": "此設定可稍後在群組設定中更改。"
|
||||
},
|
||||
"Groups": {
|
||||
"upcoming": "即將到來",
|
||||
"thisWeek": "本週",
|
||||
"earlierThisMonth": "本月稍早",
|
||||
"lastMonth": "上個月",
|
||||
"earlierThisYear": "今年稍早",
|
||||
"lastYear": "去年",
|
||||
"older": "更早"
|
||||
}
|
||||
},
|
||||
"ExpenseCard": {
|
||||
"paidBy": "由 <strong>{paidBy}</strong> 支付 <paidFor></paidFor>。",
|
||||
"receivedBy": "由 <strong>{paidBy}</strong> 收取 <paidFor></paidFor>。",
|
||||
"yourBalance": "你的餘額:"
|
||||
},
|
||||
"Groups": {
|
||||
"myGroups": "我的群組",
|
||||
"create": "建立",
|
||||
"loadingRecent": "讀取最近的群組……",
|
||||
"NoRecent": {
|
||||
"description": "你最近沒有訪問過任何群組。",
|
||||
"create": "建立一個新群組",
|
||||
"orAsk": "或請朋友發送已建立的群組鏈接。"
|
||||
},
|
||||
"recent": "最近的群組",
|
||||
"starred": "已加星標的群組",
|
||||
"archived": "已封存的群組",
|
||||
"archive": "將群組封存",
|
||||
"unarchive": "取消封存群組",
|
||||
"removeRecent": "從最近的群組中移除",
|
||||
"RecentRemovedToast": {
|
||||
"title": "群組已被移除",
|
||||
"description": "該群組已從您的最近群組列表中移除。",
|
||||
"undoAlt": "撤銷移除群組",
|
||||
"undo": "取消操作"
|
||||
},
|
||||
"AddByURL": {
|
||||
"button": "透過連結加入",
|
||||
"title": "透過連結加入群組",
|
||||
"description": "如果某個群組已與您分享,您可以在此處貼上其網址以添加到群組列表中。",
|
||||
"error": "哇哇,我們無法從您提供的網址中找到有效群組……"
|
||||
},
|
||||
"NotFound": {
|
||||
"text": "該群組不存在。",
|
||||
"link": "前往最近訪問的群組"
|
||||
}
|
||||
},
|
||||
"GroupForm": {
|
||||
"title": "群組資訊",
|
||||
"NameField": {
|
||||
"label": "群組名稱",
|
||||
"placeholder": "暑假出遊",
|
||||
"description": "輸入群組的名稱。"
|
||||
},
|
||||
"InformationField": {
|
||||
"label": "群組資訊",
|
||||
"placeholder": "對群組成員有關的資訊是什麼?"
|
||||
},
|
||||
"CurrencyField": {
|
||||
"label": "貨幣符號",
|
||||
"placeholder": "$, €, £…",
|
||||
"description": "我們根據它來顯示相應的金額。"
|
||||
},
|
||||
"Participants": {
|
||||
"title": "群組成員",
|
||||
"description": "輸入每位成員的名稱。",
|
||||
"protectedParticipant": "此成員已有登記支出,無法刪除。",
|
||||
"new": "新增",
|
||||
"add": "新增群組成員",
|
||||
"John": "林俊凱",
|
||||
"Jane": "陳怡婷",
|
||||
"Jack": "張文傑"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "客製化設定",
|
||||
"description": "這些設定是針對每台設備設置的,用於客製化您的體驗。",
|
||||
"ActiveUserField": {
|
||||
"label": "當前使用者",
|
||||
"placeholder": "選擇一位群組成員",
|
||||
"none": "無",
|
||||
"description": "用於支付消費的預設用戶"
|
||||
},
|
||||
"save": "保存",
|
||||
"saving": "保存中……",
|
||||
"create": "建立",
|
||||
"creating": "建立中……",
|
||||
"cancel": "取消"
|
||||
}
|
||||
},
|
||||
"ExpenseForm": {
|
||||
"Income": {
|
||||
"create": "新增收入",
|
||||
"edit": "編輯收入",
|
||||
"TitleField": {
|
||||
"label": "收入標題",
|
||||
"placeholder": "禮拜一晚餐",
|
||||
"description": "輸入此筆收入的描述。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "收入日期",
|
||||
"description": "輸入收到這筆收入的日期。"
|
||||
},
|
||||
"categoryFieldDescription": "選擇收入類別。",
|
||||
"paidByField": {
|
||||
"label": "接收人",
|
||||
"description": "選擇接收這筆收入的成員。"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "應接收人",
|
||||
"description": "選擇應參與此筆收入的成員。"
|
||||
},
|
||||
"splitModeDescription": "選擇如何分配此筆收入。",
|
||||
"attachDescription": "查看/附上此筆收入的收據。"
|
||||
},
|
||||
"Expense": {
|
||||
"create": "新增消費紀錄",
|
||||
"edit": "編輯消費紀錄",
|
||||
"TitleField": {
|
||||
"label": "支出標題",
|
||||
"placeholder": "週一晚餐",
|
||||
"description": "輸入此筆消費的描述。"
|
||||
},
|
||||
"DateField": {
|
||||
"label": "消費日期",
|
||||
"description": "輸入支付此消費的日期。"
|
||||
},
|
||||
"categoryFieldDescription": "選擇消費類別。",
|
||||
"paidByField": {
|
||||
"label": "支付人",
|
||||
"description": "选择支付这笔消费的群组成员。"
|
||||
},
|
||||
"paidFor": {
|
||||
"title": "應支付人",
|
||||
"description": "選擇需參與此筆消費的成員。"
|
||||
},
|
||||
"splitModeDescription": "選擇如何分配此筆消費。",
|
||||
"attachDescription": "查看/附上此筆消費的收據。"
|
||||
},
|
||||
"amountField": {
|
||||
"label": "金額"
|
||||
},
|
||||
"isReimbursementField": {
|
||||
"label": "這是一筆報銷款"
|
||||
},
|
||||
"categoryField": {
|
||||
"label": "類別"
|
||||
},
|
||||
"notesField": {
|
||||
"label": "備註"
|
||||
},
|
||||
"selectNone": "取消全選",
|
||||
"selectAll": "全選",
|
||||
"shares": "份額",
|
||||
"advancedOptions": "進階分帳選項……",
|
||||
"SplitModeField": {
|
||||
"label": "分帳方式",
|
||||
"evenly": "平均分配",
|
||||
"byShares": "自訂份額",
|
||||
"byPercentage": "自訂百分比",
|
||||
"byAmount": "自訂金額",
|
||||
"saveAsDefault": "儲存為預設分帳方式"
|
||||
},
|
||||
"DeletePopup": {
|
||||
"label": "刪除",
|
||||
"title": "要刪除這筆消費嗎?",
|
||||
"description": "確定要刪除這筆消費嗎?刪除後無法回復哦。",
|
||||
"yes": "確定",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"attachDocuments": "附件",
|
||||
"create": "新增",
|
||||
"creating": "新增中……",
|
||||
"save": "儲存",
|
||||
"saving": "儲存中……",
|
||||
"cancel": "取消",
|
||||
"reimbursement": "報銷"
|
||||
},
|
||||
"ExpenseDocumentsInput": {
|
||||
"TooBigToast": {
|
||||
"title": "文件過大",
|
||||
"description": "可以上傳的最大文件大小為 {maxSize}。這份文件大小為 {size}。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "上傳文件時發生錯誤",
|
||||
"description": "上傳文件時發生錯誤,請再試一次或更換文件。",
|
||||
"retry": "重試"
|
||||
}
|
||||
},
|
||||
"CreateFromReceipt": {
|
||||
"Dialog": {
|
||||
"triggerTitle": "從收據中新增消費紀錄",
|
||||
"title": "從收據中新增消費紀錄",
|
||||
"description": "從收據照片上抓取消費明細。",
|
||||
"body": "上傳收據的圖片,我們會試圖解析其中的支出",
|
||||
"selectImage": "選擇圖片……",
|
||||
"titleLabel": "標題:",
|
||||
"categoryLabel": "類別:",
|
||||
"amountLabel": "金額:",
|
||||
"dateLabel": "日期:",
|
||||
"editNext": "可於後續編輯消費明細。",
|
||||
"continue": "繼續"
|
||||
},
|
||||
"unknown": "未知",
|
||||
"TooBigToast": {
|
||||
"title": "文件過大",
|
||||
"description": "可以上傳的最大文件大小為 {maxSize}。這份文件大小為 {size}。"
|
||||
},
|
||||
"ErrorToast": {
|
||||
"title": "上傳文件時發生錯誤",
|
||||
"description": "上傳文件時發生錯誤,請再試一次或更換文件。",
|
||||
"retry": "重試"
|
||||
}
|
||||
},
|
||||
"Balances": {
|
||||
"title": "總覽",
|
||||
"description": "這是每個成員已支付及需支付的金額",
|
||||
"Reimbursements": {
|
||||
"title": "建議核銷",
|
||||
"description": "這是建議的銷帳方式",
|
||||
"noImbursements": "看起來你的群組目前不需要銷帳😁",
|
||||
"owes": "<strong>{from}</strong> 欠 <strong>{to}</strong>",
|
||||
"markAsPaid": "標記為已支付"
|
||||
}
|
||||
},
|
||||
"Stats": {
|
||||
"title": "統計",
|
||||
"Totals": {
|
||||
"title": "總計",
|
||||
"description": "整個群組的花費總計。",
|
||||
"groupSpendings": "群組總開銷",
|
||||
"groupEarnings": "群組總收入",
|
||||
"yourSpendings": "你的總開銷",
|
||||
"yourEarnings": "你的總收入",
|
||||
"yourShare": "你的總計份額"
|
||||
}
|
||||
},
|
||||
"Activity": {
|
||||
"title": "明細",
|
||||
"description": "群組所有活動總覽",
|
||||
"noActivity": "你的全組目前沒有任何活動",
|
||||
"someone": "某人",
|
||||
"settingsModified": "群組設定已被<strong>{participant}</strong>更改。",
|
||||
"expenseCreated": "消費 <em>{expense}</em> 由 <strong>{participant}</strong> 新增。",
|
||||
"expenseUpdated": "消費 <em>{expense}</em> 由 <strong>{participant}</strong> 更新。",
|
||||
"expenseDeleted": "消費 <em>{expense}</em> 由 <strong>{participant}</strong> 刪除。",
|
||||
"Groups": {
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"earlierThisWeek": "本週稍早",
|
||||
"lastWeek": "上週",
|
||||
"earlierThisMonth": "本月稍早",
|
||||
"lastMonth": "上個月",
|
||||
"earlierThisYear": "今年稍早",
|
||||
"lastYear": "去年",
|
||||
"older": "更早"
|
||||
}
|
||||
},
|
||||
"Information": {
|
||||
"title": "資訊",
|
||||
"description": "可在此添加群組相關資訊、公告及說明等。",
|
||||
"empty": "目前沒有群組資訊。"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "設定"
|
||||
},
|
||||
"Share": {
|
||||
"title": "分享",
|
||||
"description": "將此網址分享給其他人以加入群組並查看及新增消費紀錄",
|
||||
"warning": "警告!",
|
||||
"warningHelp": "任何有此連結的人都可以看到及編輯消費紀錄。請小心使用!"
|
||||
},
|
||||
"SchemaErrors": {
|
||||
"min1": "請輸入至少 1 個字。",
|
||||
"min2": "請輸入至少 2 個字。",
|
||||
"max5": "請輸入至少 5 個字。",
|
||||
"max50": "請輸入至少 50 個字。",
|
||||
"duplicateParticipantName": "此名稱已被使用",
|
||||
"titleRequired": "請輸入標題。",
|
||||
"invalidNumber": "數值無效。",
|
||||
"amountRequired": "必須輸入一個金額。",
|
||||
"amountNotZero": "金額不可為 0。",
|
||||
"amountTenMillion": "金額需小於 10,000,000。",
|
||||
"paidByRequired": "必須選擇一個成員。",
|
||||
"paidForMin1": "這筆消費必須包含至少一個成員。",
|
||||
"noZeroShares": "份額需大於 0。",
|
||||
"amountSum": "金額總計必須等於消費金額。",
|
||||
"percentageSum": "百分比加總必須等於 100。"
|
||||
},
|
||||
"Categories": {
|
||||
"search": "搜尋類別……",
|
||||
"noCategory": "未找到類別。",
|
||||
"Uncategorized": {
|
||||
"heading": "未分類",
|
||||
"General": "一般",
|
||||
"Payment": "支付"
|
||||
},
|
||||
"Entertainment": {
|
||||
"heading": "娛樂",
|
||||
"Entertainment": "娛樂",
|
||||
"Games": "遊戲",
|
||||
"Movies": "電影",
|
||||
"Music": "音樂",
|
||||
"Sports": "運動"
|
||||
},
|
||||
"Food and Drink": {
|
||||
"heading": "飲食",
|
||||
"Food and Drink": "飲食",
|
||||
"Dining Out": "外食",
|
||||
"Groceries": "食材",
|
||||
"Liquor": "酒水"
|
||||
},
|
||||
"Home": {
|
||||
"heading": "居家",
|
||||
"Home": "居家",
|
||||
"Electronics": "電子產品",
|
||||
"Furniture": "家具",
|
||||
"Household Supplies": "日用品",
|
||||
"Maintenance": "維護",
|
||||
"Mortgage": "貸款",
|
||||
"Pets": "寵物",
|
||||
"Rent": "租金",
|
||||
"Services": "服務"
|
||||
},
|
||||
"Life": {
|
||||
"heading": "生活",
|
||||
"Childcare": "育兒",
|
||||
"Clothing": "衣服",
|
||||
"Education": "教育",
|
||||
"Gifts": "禮物",
|
||||
"Insurance": "保險",
|
||||
"Medical Expenses": "醫療支出",
|
||||
"Taxes": "稅"
|
||||
},
|
||||
"Transportation": {
|
||||
"heading": "交通",
|
||||
"Transportation": "交通",
|
||||
"Bicycle": "自行車",
|
||||
"Bus/Train": "公車/火車",
|
||||
"Car": "汽車",
|
||||
"Gas/Fuel": "油錢/燃料",
|
||||
"Hotel": "旅館/住宿",
|
||||
"Parking": "停車",
|
||||
"Plane": "飛機",
|
||||
"Taxi": "計程車"
|
||||
},
|
||||
"Utilities": {
|
||||
"heading": "日常帳單",
|
||||
"Utilities": "日常帳單",
|
||||
"Cleaning": "清潔費",
|
||||
"Electricity": "電費",
|
||||
"Heat/Gas": "暖氣/瓦斯",
|
||||
"Trash": "垃圾費",
|
||||
"TV/Phone/Internet": "電視/電話/網路",
|
||||
"Water": "水費"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
/**
|
||||
import createNextIntlPlugin from 'next-intl/plugin'
|
||||
|
||||
const withNextIntl = createNextIntlPlugin()
|
||||
|
||||
/**
|
||||
* Undefined entries are not supported. Push optional patterns to this array only if defined.
|
||||
* @type {import('next/dist/shared/lib/image-config').RemotePattern}
|
||||
*/
|
||||
@@ -31,4 +35,4 @@ const nextConfig = {
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
export default withNextIntl(nextConfig)
|
||||
17064
package-lock.json
generated
82
package.json
@@ -6,74 +6,98 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint .",
|
||||
"check-types": "tsc --noEmit",
|
||||
"check-formatting": "prettier -c src",
|
||||
"prettier": "prettier -w src",
|
||||
"postinstall": "prisma migrate deploy && prisma generate",
|
||||
"build-image": "./scripts/build-image.sh",
|
||||
"start-container": "docker compose --env-file container.env up"
|
||||
"start-container": "docker compose --env-file container.env up",
|
||||
"test": "jest",
|
||||
"generate-currency-data": "ts-node -T ./src/scripts/generateCurrencyData.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.5.4",
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"@prisma/client": "^5.6.0",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-hover-card": "^1.0.7",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-radio-group": "^1.1.3",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@json2csv/plainjs": "^7.0.6",
|
||||
"@prisma/client": "^6.18.0",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tanstack/react-query": "^5.59.15",
|
||||
"@trpc/client": "^11.0.0-rc.586",
|
||||
"@trpc/react-query": "^11.0.0-rc.586",
|
||||
"@trpc/server": "^11.0.0-rc.586",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"client-only": "^0.0.1",
|
||||
"clsx": "^2.0.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"cmdk": "^1.1.1",
|
||||
"content-disposition": "^0.5.4",
|
||||
"dayjs": "^1.11.10",
|
||||
"embla-carousel-react": "^8.0.0-rc21",
|
||||
"lucide-react": "^0.290.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"lucide-react": "^0.501.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"next": "^14.1.0",
|
||||
"negotiator": "^0.6.3",
|
||||
"next": "^16.0.7",
|
||||
"next-intl": "^4.5.8",
|
||||
"next-s3-upload": "^0.3.4",
|
||||
"next-themes": "^0.2.1",
|
||||
"next13-progressbar": "^1.1.1",
|
||||
"openai": "^4.25.0",
|
||||
"pg": "^8.11.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.47.0",
|
||||
"react-intersection-observer": "^9.8.0",
|
||||
"prisma": "^6.18.0",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"react-hook-form": "^7.68.0",
|
||||
"react-intersection-observer": "^10.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
"sharp": "^0.33.2",
|
||||
"superjson": "^2.2.1",
|
||||
"swr": "^2.3.3",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-pattern": "^5.0.6",
|
||||
"use-debounce": "^10.0.4",
|
||||
"uuid": "^9.0.1",
|
||||
"vaul": "^0.8.0",
|
||||
"zod": "^3.22.4",
|
||||
"prisma": "^5.7.0"
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.4.8",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@total-typescript/ts-reset": "^0.5.1",
|
||||
"@types/content-disposition": "^0.5.8",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/negotiator": "^0.6.3",
|
||||
"@types/node": "^20",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/uuid": "^9.0.6",
|
||||
"autoprefixer": "^10",
|
||||
"currency-list": "^1.0.8",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "^14.1.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-next": "^16.0.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-organize-imports": "^3.2.3",
|
||||
"tailwindcss": "^3",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Group" ADD COLUMN "information" TEXT;
|
||||
@@ -0,0 +1,29 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RecurrenceRule" AS ENUM ('NONE', 'DAILY', 'WEEKLY', 'MONTHLY');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Expense" ADD COLUMN "recurrenceRule" "RecurrenceRule" DEFAULT 'NONE',
|
||||
ADD COLUMN "recurringExpenseLinkId" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "RecurringExpenseLink" (
|
||||
"id" TEXT NOT NULL,
|
||||
"groupId" TEXT NOT NULL,
|
||||
"currentFrameExpenseId" TEXT NOT NULL,
|
||||
"nextExpenseCreatedAt" TIMESTAMP(3),
|
||||
"nextExpenseDate" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "RecurringExpenseLink_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "RecurringExpenseLink_currentFrameExpenseId_key" ON "RecurringExpenseLink"("currentFrameExpenseId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "RecurringExpenseLink_groupId_idx" ON "RecurringExpenseLink"("groupId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "RecurringExpenseLink_groupId_nextExpenseCreatedAt_nextExpen_idx" ON "RecurringExpenseLink"("groupId", "nextExpenseCreatedAt", "nextExpenseDate" DESC);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RecurringExpenseLink" ADD CONSTRAINT "RecurringExpenseLink_currentFrameExpenseId_fkey" FOREIGN KEY ("currentFrameExpenseId") REFERENCES "Expense"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1 @@
|
||||
INSERT INTO "Category" ("id", "grouping", "name") VALUES (43, 'Life', 'Donation');
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Group" ADD COLUMN "currencyCode" TEXT;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Expense" ADD COLUMN "conversionRate" DECIMAL(65,30),
|
||||
ADD COLUMN "originalAmount" INTEGER,
|
||||
ADD COLUMN "originalCurrency" TEXT;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Activity" DROP CONSTRAINT "Activity_groupId_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Activity" ADD CONSTRAINT "Activity_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -14,7 +14,9 @@ datasource db {
|
||||
model Group {
|
||||
id String @id
|
||||
name String
|
||||
information String? @db.Text
|
||||
currency String @default("$")
|
||||
currencyCode String?
|
||||
participants Participant[]
|
||||
expenses Expense[]
|
||||
activities Activity[]
|
||||
@@ -38,22 +40,29 @@ model Category {
|
||||
}
|
||||
|
||||
model Expense {
|
||||
id String @id
|
||||
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
||||
expenseDate DateTime @default(dbgenerated("CURRENT_DATE")) @db.Date
|
||||
title String
|
||||
category Category? @relation(fields: [categoryId], references: [id])
|
||||
categoryId Int @default(0)
|
||||
amount Int
|
||||
paidBy Participant @relation(fields: [paidById], references: [id], onDelete: Cascade)
|
||||
paidById String
|
||||
paidFor ExpensePaidFor[]
|
||||
groupId String
|
||||
isReimbursement Boolean @default(false)
|
||||
splitMode SplitMode @default(EVENLY)
|
||||
createdAt DateTime @default(now())
|
||||
documents ExpenseDocument[]
|
||||
notes String?
|
||||
id String @id
|
||||
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
||||
expenseDate DateTime @default(dbgenerated("CURRENT_DATE")) @db.Date
|
||||
title String
|
||||
category Category? @relation(fields: [categoryId], references: [id])
|
||||
categoryId Int @default(0)
|
||||
amount Int
|
||||
originalAmount Int?
|
||||
originalCurrency String?
|
||||
conversionRate Decimal?
|
||||
paidBy Participant @relation(fields: [paidById], references: [id], onDelete: Cascade)
|
||||
paidById String
|
||||
paidFor ExpensePaidFor[]
|
||||
groupId String
|
||||
isReimbursement Boolean @default(false)
|
||||
splitMode SplitMode @default(EVENLY)
|
||||
createdAt DateTime @default(now())
|
||||
documents ExpenseDocument[]
|
||||
notes String?
|
||||
|
||||
recurrenceRule RecurrenceRule? @default(NONE)
|
||||
recurringExpenseLink RecurringExpenseLink?
|
||||
recurringExpenseLinkId String?
|
||||
}
|
||||
|
||||
model ExpenseDocument {
|
||||
@@ -72,6 +81,29 @@ enum SplitMode {
|
||||
BY_AMOUNT
|
||||
}
|
||||
|
||||
model RecurringExpenseLink {
|
||||
id String @id
|
||||
groupId String
|
||||
currentFrameExpense Expense @relation(fields: [currentFrameExpenseId], references: [id], onDelete: Cascade)
|
||||
currentFrameExpenseId String @unique
|
||||
|
||||
// Note: We do not want to link to the next expense because once it is created, it should be
|
||||
// treated as it's own independent entity. This means that if a user wants to delete an Expense
|
||||
// and any prior related recurring expenses, they'll need to delete them one by one.
|
||||
nextExpenseCreatedAt DateTime?
|
||||
nextExpenseDate DateTime
|
||||
|
||||
@@index([groupId])
|
||||
@@index([groupId, nextExpenseCreatedAt, nextExpenseDate(sort: Desc)])
|
||||
}
|
||||
|
||||
enum RecurrenceRule {
|
||||
NONE
|
||||
DAILY
|
||||
WEEKLY
|
||||
MONTHLY
|
||||
}
|
||||
|
||||
model ExpensePaidFor {
|
||||
expense Expense @relation(fields: [expenseId], references: [id], onDelete: Cascade)
|
||||
participant Participant @relation(fields: [participantId], references: [id], onDelete: Cascade)
|
||||
@@ -84,7 +116,7 @@ model ExpensePaidFor {
|
||||
|
||||
model Activity {
|
||||
id String @id
|
||||
group Group @relation(fields: [groupId], references: [id])
|
||||
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
||||
groupId String
|
||||
time DateTime @default(now())
|
||||
activityType ActivityType
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB |
BIN
public/logo/128x128.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
public/logo/144x144.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
public/logo/192x192.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
public/logo/256x256.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
public/logo/48x48.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logo/512x512-maskable.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/logo/512x512.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/logo/64x64.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
@@ -3,4 +3,4 @@
|
||||
set -euxo pipefail
|
||||
|
||||
npx prisma migrate deploy
|
||||
npm run start
|
||||
exec npm run start
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
result=$(docker ps | grep postgres)
|
||||
result=$(docker ps | grep spliit-db)
|
||||
if [ $? -eq 0 ];
|
||||
then
|
||||
echo "postgres is already running, doing nothing"
|
||||
@@ -6,6 +6,6 @@ else
|
||||
echo "postgres is not running, starting it"
|
||||
docker rm postgres --force
|
||||
mkdir -p postgres-data
|
||||
docker run --name postgres -d -p 5432:5432 -e POSTGRES_PASSWORD=1234 -v "/$(pwd)/postgres-data:/var/lib/postgresql/data" postgres
|
||||
docker run --name spliit-db -d -p 5432:5432 -e POSTGRES_PASSWORD=1234 -v "/$(pwd)/postgres-data:/var/lib/postgresql" postgres
|
||||
sleep 5 # Wait for postgres to start
|
||||
fi
|
||||
fi
|
||||
7
src/app/api/health/liveness/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { checkLiveness } from '@/lib/health'
|
||||
|
||||
// Liveness: Is the app itself healthy? (no external dependencies)
|
||||
// If this fails, Kubernetes should restart the pod
|
||||
export async function GET() {
|
||||
return checkLiveness()
|
||||
}
|
||||
7
src/app/api/health/readiness/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { checkReadiness } from '@/lib/health'
|
||||
|
||||
// Readiness: Can the app serve requests? (includes all external dependencies)
|
||||
// If this fails, Kubernetes should stop sending traffic but not restart
|
||||
export async function GET() {
|
||||
return checkReadiness()
|
||||
}
|
||||
7
src/app/api/health/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { checkReadiness } from '@/lib/health'
|
||||
|
||||
// Default health check - same as readiness (includes database check)
|
||||
// This is readiness-focused for monitoring tools like uptime-kuma
|
||||
export async function GET() {
|
||||
return checkReadiness()
|
||||
}
|
||||
13
src/app/api/trpc/[trpc]/route.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createTRPCContext } from '@/trpc/init'
|
||||
import { appRouter } from '@/trpc/routers/_app'
|
||||
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
|
||||
|
||||
const handler = (req: Request) =>
|
||||
fetchRequestHandler({
|
||||
endpoint: '/api/trpc',
|
||||
req,
|
||||
router: appRouter,
|
||||
createContext: createTRPCContext,
|
||||
})
|
||||
|
||||
export { handler as GET, handler as POST }
|
||||
@@ -41,7 +41,8 @@
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
/* --destructive: 0 62.8% 30.6%; */
|
||||
--destructive: 0 87% 47%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
|
||||
@@ -1,50 +1,44 @@
|
||||
'use client'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import { DateTimeStyle, cn, formatDate } from '@/lib/utils'
|
||||
import { Activity, ActivityType, Participant } from '@prisma/client'
|
||||
import { AppRouterOutput } from '@/trpc/routers/_app'
|
||||
import { ActivityType, Participant } from '@prisma/client'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export type Activity =
|
||||
AppRouterOutput['groups']['activities']['list']['activities'][number]
|
||||
|
||||
type Props = {
|
||||
groupId: string
|
||||
activity: Activity
|
||||
participant?: Participant
|
||||
expense?: Awaited<ReturnType<typeof getGroupExpenses>>[number]
|
||||
dateStyle: DateTimeStyle
|
||||
}
|
||||
|
||||
function getSummary(activity: Activity, participantName?: string) {
|
||||
const participant = participantName ?? 'Someone'
|
||||
function useSummary(activity: Activity, participantName?: string) {
|
||||
const t = useTranslations('Activity')
|
||||
const participant = participantName ?? t('someone')
|
||||
const expense = activity.data ?? ''
|
||||
|
||||
const tr = (key: string) =>
|
||||
t.rich(key, {
|
||||
expense,
|
||||
participant,
|
||||
em: (chunks) => <em>“{chunks}”</em>,
|
||||
strong: (chunks) => <strong>{chunks}</strong>,
|
||||
})
|
||||
|
||||
if (activity.activityType == ActivityType.UPDATE_GROUP) {
|
||||
return (
|
||||
<>
|
||||
Group settings were modified by <strong>{participant}</strong>
|
||||
</>
|
||||
)
|
||||
return <>{tr('settingsModified')}</>
|
||||
} else if (activity.activityType == ActivityType.CREATE_EXPENSE) {
|
||||
return (
|
||||
<>
|
||||
Expense <em>“{expense}”</em> created by{' '}
|
||||
<strong>{participant}</strong>.
|
||||
</>
|
||||
)
|
||||
return <>{tr('expenseCreated')}</>
|
||||
} else if (activity.activityType == ActivityType.UPDATE_EXPENSE) {
|
||||
return (
|
||||
<>
|
||||
Expense <em>“{expense}”</em> updated by{' '}
|
||||
<strong>{participant}</strong>.
|
||||
</>
|
||||
)
|
||||
return <>{tr('expenseUpdated')}</>
|
||||
} else if (activity.activityType == ActivityType.DELETE_EXPENSE) {
|
||||
return (
|
||||
<>
|
||||
Expense <em>“{expense}”</em> deleted by{' '}
|
||||
<strong>{participant}</strong>.
|
||||
</>
|
||||
)
|
||||
return <>{tr('expenseDeleted')}</>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +46,13 @@ export function ActivityItem({
|
||||
groupId,
|
||||
activity,
|
||||
participant,
|
||||
expense,
|
||||
dateStyle,
|
||||
}: Props) {
|
||||
const router = useRouter()
|
||||
const locale = useLocale()
|
||||
|
||||
const expenseExists = expense !== undefined
|
||||
const summary = getSummary(activity, participant?.name)
|
||||
const expenseExists = activity.expense !== undefined
|
||||
const summary = useSummary(activity, participant?.name)
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -75,11 +69,11 @@ export function ActivityItem({
|
||||
<div className="flex flex-col justify-between items-start">
|
||||
{dateStyle !== undefined && (
|
||||
<div className="mt-1 text-xs/5 text-muted-foreground">
|
||||
{formatDate(activity.time, { dateStyle })}
|
||||
{formatDate(activity.time, locale, { dateStyle })}
|
||||
</div>
|
||||
)}
|
||||
<div className="my-1 text-xs/5 text-muted-foreground">
|
||||
{formatDate(activity.time, { timeStyle: 'short' })}
|
||||
{formatDate(activity.time, locale, { timeStyle: 'short' })}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import { ActivityItem } from '@/app/groups/[groupId]/activity/activity-item'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import { Activity, Participant } from '@prisma/client'
|
||||
'use client'
|
||||
import {
|
||||
Activity,
|
||||
ActivityItem,
|
||||
} from '@/app/groups/[groupId]/activity/activity-item'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import dayjs, { type Dayjs } from 'dayjs'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { forwardRef, useEffect } from 'react'
|
||||
import { useInView } from 'react-intersection-observer'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
type Props = {
|
||||
groupId: string
|
||||
participants: Participant[]
|
||||
expenses: Awaited<ReturnType<typeof getGroupExpenses>>
|
||||
activities: Activity[]
|
||||
}
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
const DATE_GROUPS = {
|
||||
TODAY: 'Today',
|
||||
YESTERDAY: 'Yesterday',
|
||||
EARLIER_THIS_WEEK: 'Earlier this week',
|
||||
LAST_WEEK: 'Last week',
|
||||
EARLIER_THIS_MONTH: 'Earlier this month',
|
||||
LAST_MONTH: 'Last month',
|
||||
EARLIER_THIS_YEAR: 'Earlier this year',
|
||||
LAST_YEAR: 'Last year',
|
||||
OLDER: 'Older',
|
||||
TODAY: 'today',
|
||||
YESTERDAY: 'yesterday',
|
||||
EARLIER_THIS_WEEK: 'earlierThisWeek',
|
||||
LAST_WEEK: 'lastWeek',
|
||||
EARLIER_THIS_MONTH: 'earlierThisMonth',
|
||||
LAST_MONTH: 'lastMonth',
|
||||
EARLIER_THIS_YEAR: 'earlierThisYear',
|
||||
LAST_YEAR: 'lastYear',
|
||||
OLDER: 'older',
|
||||
}
|
||||
|
||||
function getDateGroup(date: Dayjs, today: Dayjs) {
|
||||
@@ -47,22 +50,62 @@ function getDateGroup(date: Dayjs, today: Dayjs) {
|
||||
function getGroupedActivitiesByDate(activities: Activity[]) {
|
||||
const today = dayjs()
|
||||
return activities.reduce(
|
||||
(result: { [key: string]: Activity[] }, activity: Activity) => {
|
||||
(result, activity) => {
|
||||
const activityGroup = getDateGroup(dayjs(activity.time), today)
|
||||
result[activityGroup] = result[activityGroup] ?? []
|
||||
result[activityGroup].push(activity)
|
||||
return result
|
||||
},
|
||||
{},
|
||||
{} as {
|
||||
[key: string]: Activity[]
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export function ActivityList({
|
||||
groupId,
|
||||
participants,
|
||||
expenses,
|
||||
activities,
|
||||
}: Props) {
|
||||
const ActivitiesLoading = forwardRef<HTMLDivElement>((_, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex flex-col gap-4">
|
||||
<Skeleton className="mt-2 h-3 w-24" />
|
||||
{Array(5)
|
||||
.fill(undefined)
|
||||
.map((_, index) => (
|
||||
<div key={index} className="flex gap-2 p-2">
|
||||
<div className="flex-0">
|
||||
<Skeleton className="h-3 w-12" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Skeleton className="h-3 w-48" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
ActivitiesLoading.displayName = 'ActivitiesLoading'
|
||||
|
||||
export function ActivityList() {
|
||||
const t = useTranslations('Activity')
|
||||
const { group, groupId } = useCurrentGroup()
|
||||
|
||||
const {
|
||||
data: activitiesData,
|
||||
isLoading,
|
||||
fetchNextPage,
|
||||
} = trpc.groups.activities.list.useInfiniteQuery(
|
||||
{ groupId, limit: PAGE_SIZE },
|
||||
{ getNextPageParam: ({ nextCursor }) => nextCursor },
|
||||
)
|
||||
const { ref: loadingRef, inView } = useInView()
|
||||
|
||||
const activities = activitiesData?.pages.flatMap((page) => page.activities)
|
||||
const hasMore = activitiesData?.pages.at(-1)?.hasMore ?? false
|
||||
|
||||
useEffect(() => {
|
||||
if (inView && hasMore && !isLoading) fetchNextPage()
|
||||
}, [fetchNextPage, hasMore, inView, isLoading])
|
||||
|
||||
if (isLoading || !activities || !group) return <ActivitiesLoading />
|
||||
|
||||
const groupedActivitiesByDate = getGroupedActivitiesByDate(activities)
|
||||
|
||||
return activities.length > 0 ? (
|
||||
@@ -82,31 +125,31 @@ export function ActivityList({
|
||||
'text-muted-foreground text-xs py-1 font-semibold sticky top-16 bg-white dark:bg-[#1b1917]'
|
||||
}
|
||||
>
|
||||
{dateGroup}
|
||||
{t(`Groups.${dateGroup}`)}
|
||||
</div>
|
||||
{groupActivities.map((activity: Activity) => {
|
||||
{groupActivities.map((activity) => {
|
||||
const participant =
|
||||
activity.participantId !== null
|
||||
? participants.find((p) => p.id === activity.participantId)
|
||||
: undefined
|
||||
const expense =
|
||||
activity.expenseId !== null
|
||||
? expenses.find((e) => e.id === activity.expenseId)
|
||||
? group.participants.find(
|
||||
(p) => p.id === activity.participantId,
|
||||
)
|
||||
: undefined
|
||||
return (
|
||||
<ActivityItem
|
||||
key={activity.id}
|
||||
{...{ groupId, activity, participant, expense, dateStyle }}
|
||||
groupId={groupId}
|
||||
activity={activity}
|
||||
participant={participant}
|
||||
dateStyle={dateStyle}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{hasMore && <ActivitiesLoading ref={loadingRef} />}
|
||||
</>
|
||||
) : (
|
||||
<p className="px-6 text-sm py-6">
|
||||
There is not yet any activity in your group.
|
||||
</p>
|
||||
<p className="text-sm py-6">{t('noActivity')}</p>
|
||||
)
|
||||
}
|
||||
|
||||
32
src/app/groups/[groupId]/activity/page.client.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ActivityList } from '@/app/groups/[groupId]/activity/activity-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Metadata } from 'next'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Activity',
|
||||
}
|
||||
|
||||
export function ActivityPageClient() {
|
||||
const t = useTranslations('Activity')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('title')}</CardTitle>
|
||||
<CardDescription>{t('description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col space-y-4">
|
||||
<ActivityList />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,51 +1,10 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { ActivityList } from '@/app/groups/[groupId]/activity/activity-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { getActivities, getGroupExpenses } from '@/lib/api'
|
||||
import { ActivityPageClient } from '@/app/groups/[groupId]/activity/page.client'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Activity',
|
||||
}
|
||||
|
||||
export default async function ActivityPage({
|
||||
params: { groupId },
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
const expenses = await getGroupExpenses(groupId)
|
||||
const activities = await getActivities(groupId)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Activity</CardTitle>
|
||||
<CardDescription>
|
||||
Overview of all activity in this group.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col space-y-4">
|
||||
<ActivityList
|
||||
{...{
|
||||
groupId,
|
||||
participants: group.participants,
|
||||
expenses,
|
||||
activities,
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
export default async function ActivityPage() {
|
||||
return <ActivityPageClient />
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Balances } from '@/lib/balances'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { cn, formatCurrency } from '@/lib/utils'
|
||||
import { Participant } from '@prisma/client'
|
||||
import { useLocale } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
balances: Balances
|
||||
participants: Participant[]
|
||||
currency: string
|
||||
currency: Currency
|
||||
}
|
||||
|
||||
export function BalancesList({ balances, participants, currency }: Props) {
|
||||
const locale = useLocale()
|
||||
const maxBalance = Math.max(
|
||||
...Object.values(balances).map((b) => Math.abs(b.total)),
|
||||
)
|
||||
@@ -28,7 +31,7 @@ export function BalancesList({ balances, participants, currency }: Props) {
|
||||
</div>
|
||||
<div className={cn('w-1/2 relative', isLeft || 'text-right')}>
|
||||
<div className="absolute inset-0 p-2 z-20">
|
||||
{formatCurrency(currency, balance)}
|
||||
{formatCurrency(currency, balance, locale)}
|
||||
</div>
|
||||
{balance !== 0 && (
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
'use client'
|
||||
|
||||
import { BalancesList } from '@/app/groups/[groupId]/balances-list'
|
||||
import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { getCurrencyFromGroup } from '@/lib/utils'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Fragment, useEffect } from 'react'
|
||||
import { match } from 'ts-pattern'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
export default function BalancesAndReimbursements() {
|
||||
const utils = trpc.useUtils()
|
||||
const { groupId, group } = useCurrentGroup()
|
||||
const { data: balancesData, isLoading: balancesAreLoading } =
|
||||
trpc.groups.balances.list.useQuery({
|
||||
groupId,
|
||||
})
|
||||
const t = useTranslations('Balances')
|
||||
|
||||
useEffect(() => {
|
||||
// Until we use tRPC more widely and can invalidate the cache on expense
|
||||
// update, it's easier and safer to invalidate the cache on page load.
|
||||
utils.groups.balances.invalidate()
|
||||
}, [utils])
|
||||
|
||||
const isLoading = balancesAreLoading || !balancesData || !group
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('title')}</CardTitle>
|
||||
<CardDescription>{t('description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
<BalancesLoading participantCount={group?.participants.length} />
|
||||
) : (
|
||||
<BalancesList
|
||||
balances={balancesData.balances}
|
||||
participants={group?.participants}
|
||||
currency={getCurrencyFromGroup(group)}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('Reimbursements.title')}</CardTitle>
|
||||
<CardDescription>{t('Reimbursements.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
<ReimbursementsLoading
|
||||
participantCount={group?.participants.length}
|
||||
/>
|
||||
) : (
|
||||
<ReimbursementList
|
||||
reimbursements={balancesData.reimbursements}
|
||||
participants={group?.participants}
|
||||
currency={getCurrencyFromGroup(group)}
|
||||
groupId={groupId}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ReimbursementsLoading = ({
|
||||
participantCount = 3,
|
||||
}: {
|
||||
participantCount?: number
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{Array(participantCount - 1)
|
||||
.fill(undefined)
|
||||
.map((_, index) => (
|
||||
<div key={index} className="flex justify-between py-5">
|
||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||
<Skeleton className="h-3 w-32" />
|
||||
<Skeleton className="h-3 w-24" />
|
||||
</div>
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BalancesLoading = ({
|
||||
participantCount = 3,
|
||||
}: {
|
||||
participantCount?: number
|
||||
}) => {
|
||||
const barWidth = (index: number) =>
|
||||
match(index % 3)
|
||||
.with(0, () => 'w-1/3')
|
||||
.with(1, () => 'w-2/3')
|
||||
.otherwise(() => 'w-full')
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 py-1 gap-y-2">
|
||||
{Array(participantCount)
|
||||
.fill(undefined)
|
||||
.map((_, index) =>
|
||||
index % 2 === 0 ? (
|
||||
<Fragment key={index}>
|
||||
<div className="flex items-center justify-end pr-2">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
<div className="self-start">
|
||||
<Skeleton className={`h-7 ${barWidth(index)} rounded-l-none`} />
|
||||
</div>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment key={index}>
|
||||
<div className="flex items-center justify-end">
|
||||
<Skeleton className={`h-7 ${barWidth(index)} rounded-r-none`} />
|
||||
</div>
|
||||
<div className="flex items-center pl-2">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
</Fragment>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,73 +1,10 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { BalancesList } from '@/app/groups/[groupId]/balances-list'
|
||||
import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import {
|
||||
getBalances,
|
||||
getPublicBalances,
|
||||
getSuggestedReimbursements,
|
||||
} from '@/lib/balances'
|
||||
import BalancesAndReimbursements from '@/app/groups/[groupId]/balances/balances-and-reimbursements'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Balances',
|
||||
}
|
||||
|
||||
export default async function GroupPage({
|
||||
params: { groupId },
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
const expenses = await getGroupExpenses(groupId)
|
||||
const balances = getBalances(expenses)
|
||||
const reimbursements = getSuggestedReimbursements(balances)
|
||||
const publicBalances = getPublicBalances(reimbursements)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Balances</CardTitle>
|
||||
<CardDescription>
|
||||
This is the amount that each participant paid or was paid for.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<BalancesList
|
||||
balances={publicBalances}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Suggested reimbursements</CardTitle>
|
||||
<CardDescription>
|
||||
Here are suggestions for optimized reimbursements between
|
||||
participants.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
<ReimbursementList
|
||||
reimbursements={reimbursements}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
groupId={groupId}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
export default async function GroupPage() {
|
||||
return <BalancesAndReimbursements />
|
||||
}
|
||||
|
||||
30
src/app/groups/[groupId]/current-group-context.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { AppRouterOutput } from '@/trpc/routers/_app'
|
||||
import { PropsWithChildren, createContext, useContext } from 'react'
|
||||
|
||||
type Group = NonNullable<AppRouterOutput['groups']['get']['group']>
|
||||
|
||||
type GroupContext =
|
||||
| { isLoading: false; groupId: string; group: Group }
|
||||
| { isLoading: true; groupId: string; group: undefined }
|
||||
|
||||
const CurrentGroupContext = createContext<GroupContext | null>(null)
|
||||
|
||||
export const useCurrentGroup = () => {
|
||||
const context = useContext(CurrentGroupContext)
|
||||
if (!context)
|
||||
throw new Error(
|
||||
'Missing context. Should be called inside a CurrentGroupProvider.',
|
||||
)
|
||||
return context
|
||||
}
|
||||
|
||||
export const CurrentGroupProvider = ({
|
||||
children,
|
||||
...props
|
||||
}: PropsWithChildren<GroupContext>) => {
|
||||
return (
|
||||
<CurrentGroupContext.Provider value={props}>
|
||||
{children}
|
||||
</CurrentGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
25
src/app/groups/[groupId]/edit/edit-group.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
'use client'
|
||||
|
||||
import { GroupForm } from '@/components/group-form'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
export const EditGroup = () => {
|
||||
const { groupId } = useCurrentGroup()
|
||||
const { data, isLoading } = trpc.groups.getDetails.useQuery({ groupId })
|
||||
const { mutateAsync } = trpc.groups.update.useMutation()
|
||||
const utils = trpc.useUtils()
|
||||
|
||||
if (isLoading) return <></>
|
||||
|
||||
return (
|
||||
<GroupForm
|
||||
group={data?.group}
|
||||
onSubmit={async (groupFormValues, participantId) => {
|
||||
await mutateAsync({ groupId, participantId, groupFormValues })
|
||||
await utils.groups.invalidate()
|
||||
}}
|
||||
protectedParticipantIds={data?.participantsWithExpenses}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,35 +1,10 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { GroupForm } from '@/components/group-form'
|
||||
import { getGroupExpensesParticipants, updateGroup } from '@/lib/api'
|
||||
import { groupFormSchema } from '@/lib/schemas'
|
||||
import { EditGroup } from '@/app/groups/[groupId]/edit/edit-group'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Settings',
|
||||
}
|
||||
|
||||
export default async function EditGroupPage({
|
||||
params: { groupId },
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
async function updateGroupAction(values: unknown, participantId?: string) {
|
||||
'use server'
|
||||
const groupFormValues = groupFormSchema.parse(values)
|
||||
const group = await updateGroup(groupId, groupFormValues, participantId)
|
||||
redirect(`/groups/${group.id}`)
|
||||
}
|
||||
|
||||
const protectedParticipantIds = await getGroupExpensesParticipants(groupId)
|
||||
return (
|
||||
<GroupForm
|
||||
group={group}
|
||||
onSubmit={updateGroupAction}
|
||||
protectedParticipantIds={protectedParticipantIds}
|
||||
/>
|
||||
)
|
||||
export default async function EditGroupPage() {
|
||||
return <EditGroup />
|
||||
}
|
||||
|
||||
@@ -1,55 +1,22 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { ExpenseForm } from '@/components/expense-form'
|
||||
import {
|
||||
deleteExpense,
|
||||
getCategories,
|
||||
getExpense,
|
||||
updateExpense,
|
||||
} from '@/lib/api'
|
||||
import { EditExpenseForm } from '@/app/groups/[groupId]/expenses/edit-expense-form'
|
||||
import { getRuntimeFeatureFlags } from '@/lib/featureFlags'
|
||||
import { expenseFormSchema } from '@/lib/schemas'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Edit expense',
|
||||
title: 'Edit Expense',
|
||||
}
|
||||
|
||||
export default async function EditExpensePage({
|
||||
params: { groupId, expenseId },
|
||||
params,
|
||||
}: {
|
||||
params: { groupId: string; expenseId: string }
|
||||
params: Promise<{ groupId: string; expenseId: string }>
|
||||
}) {
|
||||
const categories = await getCategories()
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
const expense = await getExpense(groupId, expenseId)
|
||||
if (!expense) notFound()
|
||||
|
||||
async function updateExpenseAction(values: unknown, participantId?: string) {
|
||||
'use server'
|
||||
const expenseFormValues = expenseFormSchema.parse(values)
|
||||
await updateExpense(groupId, expenseId, expenseFormValues, participantId)
|
||||
redirect(`/groups/${groupId}`)
|
||||
}
|
||||
|
||||
async function deleteExpenseAction(participantId?: string) {
|
||||
'use server'
|
||||
await deleteExpense(groupId, expenseId, participantId)
|
||||
redirect(`/groups/${groupId}`)
|
||||
}
|
||||
|
||||
const { groupId, expenseId } = await params
|
||||
return (
|
||||
<Suspense>
|
||||
<ExpenseForm
|
||||
group={group}
|
||||
expense={expense}
|
||||
categories={categories}
|
||||
onSubmit={updateExpenseAction}
|
||||
onDelete={deleteExpenseAction}
|
||||
runtimeFeatureFlags={await getRuntimeFeatureFlags()}
|
||||
/>
|
||||
</Suspense>
|
||||
<EditExpenseForm
|
||||
groupId={groupId}
|
||||
expenseId={expenseId}
|
||||
runtimeFeatureFlags={await getRuntimeFeatureFlags()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
'use client'
|
||||
import { Money } from '@/components/money'
|
||||
import { getBalances } from '@/lib/balances'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { useActiveUser } from '@/lib/hooks'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
groupId: string
|
||||
currency: string
|
||||
currency: Currency
|
||||
expense: Parameters<typeof getBalances>[0][number]
|
||||
}
|
||||
|
||||
export function ActiveUserBalance({ groupId, currency, expense }: Props) {
|
||||
const t = useTranslations('ExpenseCard')
|
||||
const activeUserId = useActiveUser(groupId)
|
||||
if (activeUserId === null || activeUserId === '' || activeUserId === 'None') {
|
||||
return null
|
||||
}
|
||||
|
||||
const balances = getBalances([expense])
|
||||
let fmtBalance = <>You are not involved</>
|
||||
let fmtBalance = <>{t('notInvolved')}</>
|
||||
if (Object.hasOwn(balances, activeUserId)) {
|
||||
const balance = balances[activeUserId]
|
||||
let balanceDetail = <></>
|
||||
@@ -33,7 +36,7 @@ export function ActiveUserBalance({ groupId, currency, expense }: Props) {
|
||||
}
|
||||
fmtBalance = (
|
||||
<>
|
||||
Your balance:{' '}
|
||||
{t('yourBalance')}{' '}
|
||||
<Money {...{ currency, amount: balance.total }} bold colored />
|
||||
{balanceDetail}
|
||||
</>
|
||||
|
||||
@@ -12,27 +12,30 @@ import {
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from '@/components/ui/drawer'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||
import { getGroup } from '@/lib/api'
|
||||
import { useMediaQuery } from '@/lib/hooks'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { AppRouterOutput } from '@/trpc/routers/_app'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { ComponentProps, useEffect, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||
}
|
||||
|
||||
export function ActiveUserModal({ group }: Props) {
|
||||
export function ActiveUserModal({ groupId }: { groupId: string }) {
|
||||
const t = useTranslations('Expenses.ActiveUserModal')
|
||||
const [open, setOpen] = useState(false)
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)')
|
||||
const { data: groupData } = trpc.groups.get.useQuery({ groupId })
|
||||
|
||||
const group = groupData?.group
|
||||
|
||||
useEffect(() => {
|
||||
if (!group) return
|
||||
|
||||
const tempUser = localStorage.getItem(`newGroup-activeUser`)
|
||||
const activeUser = localStorage.getItem(`${group.id}-activeUser`)
|
||||
if (!tempUser && !activeUser) {
|
||||
@@ -41,6 +44,8 @@ export function ActiveUserModal({ group }: Props) {
|
||||
}, [group])
|
||||
|
||||
function updateOpen(open: boolean) {
|
||||
if (!group) return
|
||||
|
||||
if (!open && !localStorage.getItem(`${group.id}-activeUser`)) {
|
||||
localStorage.setItem(`${group.id}-activeUser`, 'None')
|
||||
}
|
||||
@@ -52,16 +57,13 @@ export function ActiveUserModal({ group }: Props) {
|
||||
<Dialog open={open} onOpenChange={updateOpen}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Who are you?</DialogTitle>
|
||||
<DialogDescription>
|
||||
Tell us which participant you are to let us customize how the
|
||||
information is displayed.
|
||||
</DialogDescription>
|
||||
<DialogTitle>{t('title')}</DialogTitle>
|
||||
<DialogDescription>{t('description')}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<ActiveUserForm group={group} close={() => setOpen(false)} />
|
||||
<DialogFooter className="sm:justify-center">
|
||||
<p className="text-sm text-center text-muted-foreground">
|
||||
This setting can be changed later in the group settings.
|
||||
{t('footer')}
|
||||
</p>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
@@ -73,11 +75,8 @@ export function ActiveUserModal({ group }: Props) {
|
||||
<Drawer open={open} onOpenChange={updateOpen}>
|
||||
<DrawerContent>
|
||||
<DrawerHeader className="text-left">
|
||||
<DrawerTitle>Who are you?</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Tell us which participant you are to let us customize how the
|
||||
information is displayed.
|
||||
</DrawerDescription>
|
||||
<DrawerTitle>{t('title')}</DrawerTitle>
|
||||
<DialogDescription>{t('description')}</DialogDescription>
|
||||
</DrawerHeader>
|
||||
<ActiveUserForm
|
||||
className="px-4"
|
||||
@@ -86,7 +85,7 @@ export function ActiveUserModal({ group }: Props) {
|
||||
/>
|
||||
<DrawerFooter className="pt-2">
|
||||
<p className="text-sm text-center text-muted-foreground">
|
||||
This setting can be changed later in the group settings.
|
||||
{t('footer')}
|
||||
</p>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
@@ -98,13 +97,19 @@ function ActiveUserForm({
|
||||
group,
|
||||
close,
|
||||
className,
|
||||
}: ComponentProps<'form'> & { group: Props['group']; close: () => void }) {
|
||||
}: ComponentProps<'form'> & {
|
||||
group?: AppRouterOutput['groups']['get']['group']
|
||||
close: () => void
|
||||
}) {
|
||||
const t = useTranslations('Expenses.ActiveUserModal')
|
||||
const [selected, setSelected] = useState('None')
|
||||
|
||||
return (
|
||||
<form
|
||||
className={cn('grid items-start gap-4', className)}
|
||||
onSubmit={(event) => {
|
||||
if (!group) return
|
||||
|
||||
event.preventDefault()
|
||||
localStorage.setItem(`${group.id}-activeUser`, selected)
|
||||
close()
|
||||
@@ -115,10 +120,10 @@ function ActiveUserForm({
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="none" id="none" />
|
||||
<Label htmlFor="none" className="italic font-normal flex-1">
|
||||
I don’t want to select anyone
|
||||
{t('nobody')}
|
||||
</Label>
|
||||
</div>
|
||||
{group.participants.map((participant) => (
|
||||
{group?.participants.map((participant) => (
|
||||
<div key={participant.id} className="flex items-center space-x-2">
|
||||
<RadioGroupItem value={participant.id} id={participant.id} />
|
||||
<Label htmlFor={participant.id} className="flex-1">
|
||||
@@ -128,7 +133,7 @@ function ActiveUserForm({
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<Button type="submit">Save changes</Button>
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
FerrisWheel,
|
||||
Fuel,
|
||||
Gift,
|
||||
HandHelping,
|
||||
Home,
|
||||
Hotel,
|
||||
Lamp,
|
||||
@@ -47,6 +48,7 @@ export function CategoryIcon({
|
||||
...props
|
||||
}: { category: Category | null } & LucideProps) {
|
||||
const Icon = getCategoryIcon(`${category?.grouping}/${category?.name}`)
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <Icon {...props} />
|
||||
}
|
||||
|
||||
@@ -96,6 +98,8 @@ function getCategoryIcon(category: string): LucideIcon {
|
||||
return Baby
|
||||
case 'Life/Clothing':
|
||||
return Shirt
|
||||
case 'Life/Donation':
|
||||
return HandHelping
|
||||
case 'Life/Education':
|
||||
return LibraryBig
|
||||
case 'Life/Gifts':
|
||||
|
||||
45
src/app/groups/[groupId]/expenses/create-expense-form.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client'
|
||||
import { RuntimeFeatureFlags } from '@/lib/featureFlags'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { ExpenseForm } from './expense-form'
|
||||
|
||||
export function CreateExpenseForm({
|
||||
groupId,
|
||||
runtimeFeatureFlags,
|
||||
}: {
|
||||
groupId: string
|
||||
expenseId?: string
|
||||
runtimeFeatureFlags: RuntimeFeatureFlags
|
||||
}) {
|
||||
const { data: groupData } = trpc.groups.get.useQuery({ groupId })
|
||||
const group = groupData?.group
|
||||
|
||||
const { data: categoriesData } = trpc.categories.list.useQuery()
|
||||
const categories = categoriesData?.categories
|
||||
|
||||
const { mutateAsync: createExpenseMutateAsync } =
|
||||
trpc.groups.expenses.create.useMutation()
|
||||
|
||||
const utils = trpc.useUtils()
|
||||
const router = useRouter()
|
||||
|
||||
if (!group || !categories) return null
|
||||
|
||||
return (
|
||||
<ExpenseForm
|
||||
group={group}
|
||||
categories={categories}
|
||||
onSubmit={async (expenseFormValues, participantId) => {
|
||||
await createExpenseMutateAsync({
|
||||
groupId,
|
||||
expenseFormValues,
|
||||
participantId,
|
||||
})
|
||||
utils.groups.expenses.invalidate()
|
||||
router.push(`/groups/${group.id}`)
|
||||
}}
|
||||
runtimeFeatureFlags={runtimeFeatureFlags}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export async function extractExpenseInformationFromImage(imageUrl: string) {
|
||||
const categories = await getCategories()
|
||||
|
||||
const body: ChatCompletionCreateParamsNonStreaming = {
|
||||
model: 'gpt-4-vision-preview',
|
||||
model: 'gpt-5-nano',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
@@ -22,7 +22,7 @@ export async function extractExpenseInformationFromImage(imageUrl: string) {
|
||||
text: `
|
||||
This image contains a receipt.
|
||||
Read the total amount and store it as a non-formatted number without any other text or currency.
|
||||
Then guess the category for this receipt amoung the following categories and store its ID: ${categories.map(
|
||||
Then guess the category for this receipt among the following categories and store its ID: ${categories.map(
|
||||
(category) => formatCategoryForAIPrompt(category),
|
||||
)}.
|
||||
Guess the expense’s date and store it as yyyy-mm-dd.
|
||||
|
||||
@@ -26,27 +26,64 @@ import {
|
||||
import { ToastAction } from '@/components/ui/toast'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { useMediaQuery } from '@/lib/hooks'
|
||||
import { formatCurrency, formatDate, formatFileSize } from '@/lib/utils'
|
||||
import { Category } from '@prisma/client'
|
||||
import {
|
||||
formatCurrency,
|
||||
formatDate,
|
||||
formatFileSize,
|
||||
getCurrencyFromGroup,
|
||||
} from '@/lib/utils'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { ChevronRight, FileQuestion, Loader2, Receipt } from 'lucide-react'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import { getImageData, usePresignedUpload } from 'next-s3-upload'
|
||||
import Image from 'next/image'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { PropsWithChildren, ReactNode, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
groupId: string
|
||||
groupCurrency: string
|
||||
categories: Category[]
|
||||
}
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
const MAX_FILE_SIZE = 5 * 1024 ** 2
|
||||
|
||||
export function CreateFromReceiptButton({
|
||||
groupId,
|
||||
groupCurrency,
|
||||
categories,
|
||||
}: Props) {
|
||||
export function CreateFromReceiptButton() {
|
||||
const t = useTranslations('CreateFromReceipt')
|
||||
const isDesktop = useMediaQuery('(min-width: 640px)')
|
||||
|
||||
const DialogOrDrawer = isDesktop
|
||||
? CreateFromReceiptDialog
|
||||
: CreateFromReceiptDrawer
|
||||
|
||||
return (
|
||||
<DialogOrDrawer
|
||||
trigger={
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
title={t('Dialog.triggerTitle')}
|
||||
>
|
||||
<Receipt className="w-4 h-4" />
|
||||
</Button>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
<span>{t('Dialog.title')}</span>
|
||||
<Badge className="bg-pink-700 hover:bg-pink-600 dark:bg-pink-500 dark:hover:bg-pink-600">
|
||||
Beta
|
||||
</Badge>
|
||||
</>
|
||||
}
|
||||
description={<>{t('Dialog.description')}</>}
|
||||
>
|
||||
<ReceiptDialogContent />
|
||||
</DialogOrDrawer>
|
||||
)
|
||||
}
|
||||
|
||||
function ReceiptDialogContent() {
|
||||
const { group } = useCurrentGroup()
|
||||
const { data: categoriesData } = trpc.categories.list.useQuery()
|
||||
const categories = categoriesData?.categories
|
||||
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('CreateFromReceipt')
|
||||
const [pending, setPending] = useState(false)
|
||||
const { uploadToS3, FileInput, openFileDialog } = usePresignedUpload()
|
||||
const { toast } = useToast()
|
||||
@@ -55,15 +92,15 @@ export function CreateFromReceiptButton({
|
||||
| null
|
||||
| (ReceiptExtractedInfo & { url: string; width?: number; height?: number })
|
||||
>(null)
|
||||
const isDesktop = useMediaQuery('(min-width: 640px)')
|
||||
|
||||
const handleFileChange = async (file: File) => {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
toast({
|
||||
title: 'The file is too big',
|
||||
description: `The maximum file size you can upload is ${formatFileSize(
|
||||
MAX_FILE_SIZE,
|
||||
)}. Yours is ${formatFileSize(file.size)}.`,
|
||||
title: t('TooBigToast.title'),
|
||||
description: t('TooBigToast.description', {
|
||||
maxSize: formatFileSize(MAX_FILE_SIZE, locale),
|
||||
size: formatFileSize(file.size, locale),
|
||||
}),
|
||||
variant: 'destructive',
|
||||
})
|
||||
return
|
||||
@@ -82,13 +119,15 @@ export function CreateFromReceiptButton({
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
toast({
|
||||
title: 'Error while uploading document',
|
||||
description:
|
||||
'Something wrong happened when uploading the document. Please retry later or select a different file.',
|
||||
title: t('ErrorToast.title'),
|
||||
description: t('ErrorToast.description'),
|
||||
variant: 'destructive',
|
||||
action: (
|
||||
<ToastAction altText="Retry" onClick={() => upload()}>
|
||||
Retry
|
||||
<ToastAction
|
||||
altText={t('ErrorToast.retry')}
|
||||
onClick={() => upload()}
|
||||
>
|
||||
{t('ErrorToast.retry')}
|
||||
</ToastAction>
|
||||
),
|
||||
})
|
||||
@@ -101,162 +140,139 @@ export function CreateFromReceiptButton({
|
||||
|
||||
const receiptInfoCategory =
|
||||
(receiptInfo?.categoryId &&
|
||||
categories.find((c) => String(c.id) === receiptInfo.categoryId)) ||
|
||||
categories?.find((c) => String(c.id) === receiptInfo.categoryId)) ||
|
||||
null
|
||||
|
||||
const DialogOrDrawer = isDesktop
|
||||
? CreateFromReceiptDialog
|
||||
: CreateFromReceiptDrawer
|
||||
|
||||
return (
|
||||
<DialogOrDrawer
|
||||
trigger={
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
title="Create expense from receipt"
|
||||
>
|
||||
<Receipt className="w-4 h-4" />
|
||||
</Button>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
<span>Create from receipt</span>
|
||||
<Badge className="bg-pink-700 hover:bg-pink-600 dark:bg-pink-500 dark:hover:bg-pink-600">
|
||||
Beta
|
||||
</Badge>
|
||||
</>
|
||||
}
|
||||
description={<>Extract the expense information from a receipt photo.</>}
|
||||
>
|
||||
<div className="prose prose-sm dark:prose-invert">
|
||||
<p>
|
||||
Upload the photo of a receipt, and we’ll scan it to extract the
|
||||
expense information if we can.
|
||||
</p>
|
||||
<div>
|
||||
<FileInput
|
||||
onChange={handleFileChange}
|
||||
accept="image/jpeg,image/png"
|
||||
/>
|
||||
<div className="grid gap-x-4 gap-y-2 grid-cols-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="row-span-3 w-full h-full relative"
|
||||
title="Create expense from receipt"
|
||||
onClick={openFileDialog}
|
||||
disabled={pending}
|
||||
>
|
||||
{pending ? (
|
||||
<Loader2 className="w-8 h-8 animate-spin" />
|
||||
) : receiptInfo ? (
|
||||
<div className="absolute top-2 left-2 bottom-2 right-2">
|
||||
<Image
|
||||
src={receiptInfo.url}
|
||||
width={receiptInfo.width}
|
||||
height={receiptInfo.height}
|
||||
className="w-full h-full m-0 object-contain drop-shadow-lg"
|
||||
alt="Scanned receipt"
|
||||
/>
|
||||
</div>
|
||||
<div className="prose prose-sm dark:prose-invert">
|
||||
<p>{t('Dialog.body')}</p>
|
||||
<div>
|
||||
<FileInput onChange={handleFileChange} accept="image/jpeg,image/png" />
|
||||
<div className="grid gap-x-4 gap-y-2 grid-cols-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="row-span-3 w-full h-full relative"
|
||||
title="Create expense from receipt"
|
||||
onClick={openFileDialog}
|
||||
disabled={pending}
|
||||
>
|
||||
{pending ? (
|
||||
<Loader2 className="w-8 h-8 animate-spin" />
|
||||
) : receiptInfo ? (
|
||||
<div className="absolute top-2 left-2 bottom-2 right-2">
|
||||
<Image
|
||||
src={receiptInfo.url}
|
||||
width={receiptInfo.width}
|
||||
height={receiptInfo.height}
|
||||
className="w-full h-full m-0 object-contain drop-shadow-lg"
|
||||
alt="Scanned receipt"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs sm:text-sm text-muted-foreground">
|
||||
{t('Dialog.selectImage')}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
<div className="col-span-2">
|
||||
<strong>{t('Dialog.titleLabel')}</strong>
|
||||
<div>{receiptInfo ? receiptInfo.title ?? <Unknown /> : '…'}</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<strong>{t('Dialog.categoryLabel')}</strong>
|
||||
<div>
|
||||
{receiptInfo ? (
|
||||
receiptInfoCategory ? (
|
||||
<div className="flex items-center">
|
||||
<CategoryIcon
|
||||
category={receiptInfoCategory}
|
||||
className="inline w-4 h-4 mr-2"
|
||||
/>
|
||||
<span className="mr-1">{receiptInfoCategory.grouping}</span>
|
||||
<ChevronRight className="inline w-3 h-3 mr-1" />
|
||||
<span>{receiptInfoCategory.name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<Unknown />
|
||||
)
|
||||
) : (
|
||||
<span className="text-xs sm:text-sm text-muted-foreground">
|
||||
Select image…
|
||||
</span>
|
||||
''
|
||||
)}
|
||||
</Button>
|
||||
<div className="col-span-2">
|
||||
<strong>Title:</strong>
|
||||
<div>{receiptInfo ? receiptInfo.title ?? <Unknown /> : '…'}</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<strong>Category:</strong>
|
||||
<div>
|
||||
{receiptInfo ? (
|
||||
receiptInfoCategory ? (
|
||||
<div className="flex items-center">
|
||||
<CategoryIcon
|
||||
category={receiptInfoCategory}
|
||||
className="inline w-4 h-4 mr-2"
|
||||
/>
|
||||
<span className="mr-1">
|
||||
{receiptInfoCategory.grouping}
|
||||
</span>
|
||||
<ChevronRight className="inline w-3 h-3 mr-1" />
|
||||
<span>{receiptInfoCategory.name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<Unknown />
|
||||
)
|
||||
) : (
|
||||
'' || '…'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{t('Dialog.amountLabel')}</strong>
|
||||
<div>
|
||||
<strong>Amount:</strong>
|
||||
<div>
|
||||
{receiptInfo ? (
|
||||
receiptInfo.amount ? (
|
||||
<>{formatCurrency(groupCurrency, receiptInfo.amount)}</>
|
||||
) : (
|
||||
<Unknown />
|
||||
)
|
||||
{receiptInfo && group ? (
|
||||
receiptInfo.amount ? (
|
||||
<>
|
||||
{formatCurrency(
|
||||
getCurrencyFromGroup(group),
|
||||
receiptInfo.amount,
|
||||
locale,
|
||||
true,
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
'…'
|
||||
)}
|
||||
</div>
|
||||
<Unknown />
|
||||
)
|
||||
) : (
|
||||
'…'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{t('Dialog.dateLabel')}</strong>
|
||||
<div>
|
||||
<strong>Date:</strong>
|
||||
<div>
|
||||
{receiptInfo ? (
|
||||
receiptInfo.date ? (
|
||||
formatDate(new Date(`${receiptInfo?.date}T12:00:00.000Z`), {
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
) : (
|
||||
<Unknown />
|
||||
{receiptInfo ? (
|
||||
receiptInfo.date ? (
|
||||
formatDate(
|
||||
new Date(`${receiptInfo?.date}T12:00:00.000Z`),
|
||||
locale,
|
||||
{ dateStyle: 'medium' },
|
||||
)
|
||||
) : (
|
||||
'…'
|
||||
)}
|
||||
</div>
|
||||
<Unknown />
|
||||
)
|
||||
) : (
|
||||
'…'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>You’ll be able to edit the expense information next.</p>
|
||||
<div className="text-center">
|
||||
<Button
|
||||
disabled={pending || !receiptInfo}
|
||||
onClick={() => {
|
||||
if (!receiptInfo) return
|
||||
router.push(
|
||||
`/groups/${groupId}/expenses/create?amount=${
|
||||
receiptInfo.amount
|
||||
}&categoryId=${receiptInfo.categoryId}&date=${
|
||||
receiptInfo.date
|
||||
}&title=${encodeURIComponent(
|
||||
receiptInfo.title ?? '',
|
||||
)}&imageUrl=${encodeURIComponent(receiptInfo.url)}&imageWidth=${
|
||||
receiptInfo.width
|
||||
}&imageHeight=${receiptInfo.height}`,
|
||||
)
|
||||
}}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogOrDrawer>
|
||||
<p>{t('Dialog.editNext')}</p>
|
||||
<div className="text-center">
|
||||
<Button
|
||||
disabled={pending || !receiptInfo}
|
||||
onClick={() => {
|
||||
if (!receiptInfo || !group) return
|
||||
router.push(
|
||||
`/groups/${group.id}/expenses/create?amount=${
|
||||
receiptInfo.amount
|
||||
}&categoryId=${receiptInfo.categoryId}&date=${
|
||||
receiptInfo.date
|
||||
}&title=${encodeURIComponent(
|
||||
receiptInfo.title ?? '',
|
||||
)}&imageUrl=${encodeURIComponent(receiptInfo.url)}&imageWidth=${
|
||||
receiptInfo.width
|
||||
}&imageHeight=${receiptInfo.height}`,
|
||||
)
|
||||
}}
|
||||
>
|
||||
{t('Dialog.continue')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Unknown() {
|
||||
const t = useTranslations('CreateFromReceipt')
|
||||
return (
|
||||
<div className="flex gap-1 items-center text-muted-foreground">
|
||||
<FileQuestion className="w-4 h-4" />
|
||||
<em>Unknown</em>
|
||||
<em>{t('unknown')}</em>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,40 +1,21 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { ExpenseForm } from '@/components/expense-form'
|
||||
import { createExpense, getCategories } from '@/lib/api'
|
||||
import { CreateExpenseForm } from '@/app/groups/[groupId]/expenses/create-expense-form'
|
||||
import { getRuntimeFeatureFlags } from '@/lib/featureFlags'
|
||||
import { expenseFormSchema } from '@/lib/schemas'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create expense',
|
||||
title: 'Create Expense',
|
||||
}
|
||||
|
||||
export default async function ExpensePage({
|
||||
params: { groupId },
|
||||
params,
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
params: Promise<{ groupId: string }>
|
||||
}) {
|
||||
const categories = await getCategories()
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
async function createExpenseAction(values: unknown, participantId?: string) {
|
||||
'use server'
|
||||
const expenseFormValues = expenseFormSchema.parse(values)
|
||||
await createExpense(expenseFormValues, groupId, participantId)
|
||||
redirect(`/groups/${groupId}`)
|
||||
}
|
||||
|
||||
const { groupId } = await params
|
||||
return (
|
||||
<Suspense>
|
||||
<ExpenseForm
|
||||
group={group}
|
||||
categories={categories}
|
||||
onSubmit={createExpenseAction}
|
||||
runtimeFeatureFlags={await getRuntimeFeatureFlags()}
|
||||
/>
|
||||
</Suspense>
|
||||
<CreateExpenseForm
|
||||
groupId={groupId}
|
||||
runtimeFeatureFlags={await getRuntimeFeatureFlags()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
11
src/app/groups/[groupId]/expenses/documents-count.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Paperclip } from 'lucide-react'
|
||||
|
||||
export function DocumentsCount({ count }: { count: number }) {
|
||||
if (count === 0) return <></>
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Paperclip className="w-3.5 h-3.5 mr-1 mt-0.5 text-muted-foreground" />
|
||||
<span>{count}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
65
src/app/groups/[groupId]/expenses/edit-expense-form.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
import { RuntimeFeatureFlags } from '@/lib/featureFlags'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { ExpenseForm } from './expense-form'
|
||||
|
||||
export function EditExpenseForm({
|
||||
groupId,
|
||||
expenseId,
|
||||
runtimeFeatureFlags,
|
||||
}: {
|
||||
groupId: string
|
||||
expenseId: string
|
||||
runtimeFeatureFlags: RuntimeFeatureFlags
|
||||
}) {
|
||||
const { data: groupData } = trpc.groups.get.useQuery({ groupId })
|
||||
const group = groupData?.group
|
||||
|
||||
const { data: categoriesData } = trpc.categories.list.useQuery()
|
||||
const categories = categoriesData?.categories
|
||||
|
||||
const { data: expenseData } = trpc.groups.expenses.get.useQuery({
|
||||
groupId,
|
||||
expenseId,
|
||||
})
|
||||
const expense = expenseData?.expense
|
||||
|
||||
const { mutateAsync: updateExpenseMutateAsync } =
|
||||
trpc.groups.expenses.update.useMutation()
|
||||
const { mutateAsync: deleteExpenseMutateAsync } =
|
||||
trpc.groups.expenses.delete.useMutation()
|
||||
|
||||
const utils = trpc.useUtils()
|
||||
const router = useRouter()
|
||||
|
||||
if (!group || !categories || !expense) return null
|
||||
|
||||
return (
|
||||
<ExpenseForm
|
||||
group={group}
|
||||
expense={expense}
|
||||
categories={categories}
|
||||
onSubmit={async (expenseFormValues, participantId) => {
|
||||
await updateExpenseMutateAsync({
|
||||
expenseId,
|
||||
groupId,
|
||||
expenseFormValues,
|
||||
participantId,
|
||||
})
|
||||
utils.groups.expenses.invalidate()
|
||||
router.push(`/groups/${group.id}`)
|
||||
}}
|
||||
onDelete={async (participantId) => {
|
||||
await deleteExpenseMutateAsync({
|
||||
expenseId,
|
||||
groupId,
|
||||
participantId,
|
||||
})
|
||||
utils.groups.expenses.invalidate()
|
||||
router.push(`/groups/${group.id}`)
|
||||
}}
|
||||
runtimeFeatureFlags={runtimeFeatureFlags}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,64 @@
|
||||
'use client'
|
||||
import { ActiveUserBalance } from '@/app/groups/[groupId]/expenses/active-user-balance'
|
||||
import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon'
|
||||
import { DocumentsCount } from '@/app/groups/[groupId]/expenses/documents-count'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import { cn, formatCurrency, formatDate } from '@/lib/utils'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { cn, formatCurrency, formatDateOnly } from '@/lib/utils'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Fragment } from 'react'
|
||||
|
||||
type Props = {
|
||||
expense: Awaited<ReturnType<typeof getGroupExpenses>>[number]
|
||||
currency: string
|
||||
groupId: string
|
||||
type Expense = Awaited<ReturnType<typeof getGroupExpenses>>[number]
|
||||
|
||||
function Participants({
|
||||
expense,
|
||||
participantCount,
|
||||
}: {
|
||||
expense: Expense
|
||||
participantCount: number
|
||||
}) {
|
||||
const t = useTranslations('ExpenseCard')
|
||||
const key = expense.amount > 0 ? 'paidBy' : 'receivedBy'
|
||||
const paidFor =
|
||||
expense.paidFor.length == participantCount && participantCount >= 4 ? (
|
||||
<strong>{t('everyone')}</strong>
|
||||
) : (
|
||||
expense.paidFor.map((paidFor, index) => (
|
||||
<Fragment key={index}>
|
||||
{index !== 0 && <>, </>}
|
||||
<strong>{paidFor.participant.name}</strong>
|
||||
</Fragment>
|
||||
))
|
||||
)
|
||||
|
||||
const participants = t.rich(key, {
|
||||
strong: (chunks) => <strong>{chunks}</strong>,
|
||||
paidBy: expense.paidBy.name,
|
||||
paidFor: () => paidFor,
|
||||
forCount: expense.paidFor.length,
|
||||
})
|
||||
return <>{participants}</>
|
||||
}
|
||||
|
||||
export function ExpenseCard({ expense, currency, groupId }: Props) {
|
||||
type Props = {
|
||||
expense: Expense
|
||||
currency: Currency
|
||||
groupId: string
|
||||
participantCount: number
|
||||
}
|
||||
|
||||
export function ExpenseCard({
|
||||
expense,
|
||||
currency,
|
||||
groupId,
|
||||
participantCount,
|
||||
}: Props) {
|
||||
const router = useRouter()
|
||||
const locale = useLocale()
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -38,14 +80,7 @@ export function ExpenseCard({ expense, currency, groupId }: Props) {
|
||||
{expense.title}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{expense.amount > 0 ? 'Paid by ' : 'Received by '}
|
||||
<strong>{expense.paidBy.name}</strong> for{' '}
|
||||
{expense.paidFor.map((paidFor, index) => (
|
||||
<Fragment key={index}>
|
||||
{index !== 0 && <>, </>}
|
||||
<strong>{paidFor.participant.name}</strong>
|
||||
</Fragment>
|
||||
))}
|
||||
<Participants expense={expense} participantCount={participantCount} />
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<ActiveUserBalance {...{ groupId, currency, expense }} />
|
||||
@@ -58,10 +93,13 @@ export function ExpenseCard({ expense, currency, groupId }: Props) {
|
||||
expense.isReimbursement ? 'italic' : 'font-bold',
|
||||
)}
|
||||
>
|
||||
{formatCurrency(currency, expense.amount)}
|
||||
{formatCurrency(currency, expense.amount, locale)}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{formatDate(expense.expenseDate, { dateStyle: 'medium' })}
|
||||
<DocumentsCount count={expense._count.documents} />
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{formatDateOnly(expense.expenseDate, locale, { dateStyle: 'medium' })}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
||||
1289
src/app/groups/[groupId]/expenses/expense-form.tsx
Normal file
@@ -4,32 +4,30 @@ import { getGroupExpensesAction } from '@/app/groups/[groupId]/expenses/expense-
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SearchBar } from '@/components/ui/search-bar'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Participant } from '@prisma/client'
|
||||
import { getCurrencyFromGroup } from '@/lib/utils'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import dayjs, { type Dayjs } from 'dayjs'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { forwardRef, useEffect, useMemo, useState } from 'react'
|
||||
import { useInView } from 'react-intersection-observer'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
type ExpensesType = NonNullable<
|
||||
Awaited<ReturnType<typeof getGroupExpensesAction>>
|
||||
>
|
||||
|
||||
type Props = {
|
||||
expensesFirstPage: ExpensesType
|
||||
expenseCount: number
|
||||
participants: Participant[]
|
||||
currency: string
|
||||
groupId: string
|
||||
}
|
||||
|
||||
const EXPENSE_GROUPS = {
|
||||
UPCOMING: 'Upcoming',
|
||||
THIS_WEEK: 'This week',
|
||||
EARLIER_THIS_MONTH: 'Earlier this month',
|
||||
LAST_MONTH: 'Last month',
|
||||
EARLIER_THIS_YEAR: 'Earlier this year',
|
||||
LAST_YEAR: 'Last year',
|
||||
OLDER: 'Older',
|
||||
UPCOMING: 'upcoming',
|
||||
THIS_WEEK: 'thisWeek',
|
||||
EARLIER_THIS_MONTH: 'earlierThisMonth',
|
||||
LAST_MONTH: 'lastMonth',
|
||||
EARLIER_THIS_YEAR: 'earlierThisYear',
|
||||
LAST_YEAR: 'lastYear',
|
||||
OLDER: 'older',
|
||||
}
|
||||
|
||||
function getExpenseGroup(date: Dayjs, today: Dayjs) {
|
||||
@@ -60,23 +58,16 @@ function getGroupedExpensesByDate(expenses: ExpensesType) {
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function ExpenseList({
|
||||
expensesFirstPage,
|
||||
expenseCount,
|
||||
currency,
|
||||
participants,
|
||||
groupId,
|
||||
}: Props) {
|
||||
const firstLen = expensesFirstPage.length
|
||||
export function ExpenseList() {
|
||||
const { groupId, group } = useCurrentGroup()
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [dataIndex, setDataIndex] = useState(firstLen)
|
||||
const [dataLen, setDataLen] = useState(firstLen)
|
||||
const [hasMoreData, setHasMoreData] = useState(expenseCount > firstLen)
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [expenses, setExpenses] = useState(expensesFirstPage)
|
||||
const { ref, inView } = useInView()
|
||||
const [debouncedSearchText] = useDebounce(searchText, 300)
|
||||
|
||||
const participants = group?.participants
|
||||
|
||||
useEffect(() => {
|
||||
if (!participants) return
|
||||
|
||||
const activeUser = localStorage.getItem('newGroup-activeUser')
|
||||
const newUser = localStorage.getItem(`${groupId}-newUser`)
|
||||
if (activeUser || newUser) {
|
||||
@@ -95,55 +86,77 @@ export function ExpenseList({
|
||||
}
|
||||
}, [groupId, participants])
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchBar onValueChange={(value) => setSearchText(value)} />
|
||||
<ExpenseListForSearch
|
||||
groupId={groupId}
|
||||
searchText={debouncedSearchText}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ExpenseListForSearch = ({
|
||||
groupId,
|
||||
searchText,
|
||||
}: {
|
||||
groupId: string
|
||||
searchText: string
|
||||
}) => {
|
||||
const utils = trpc.useUtils()
|
||||
const { group } = useCurrentGroup()
|
||||
|
||||
useEffect(() => {
|
||||
const fetchNextPage = async () => {
|
||||
setIsFetching(true)
|
||||
// Until we use tRPC more widely and can invalidate the cache on expense
|
||||
// update, it's easier and safer to invalidate the cache on page load.
|
||||
utils.groups.expenses.invalidate()
|
||||
}, [utils])
|
||||
|
||||
const newExpenses = await getGroupExpensesAction(groupId, {
|
||||
offset: dataIndex,
|
||||
length: dataLen,
|
||||
})
|
||||
const t = useTranslations('Expenses')
|
||||
const { ref: loadingRef, inView } = useInView()
|
||||
|
||||
if (newExpenses !== null) {
|
||||
const exp = expenses.concat(newExpenses)
|
||||
setExpenses(exp)
|
||||
setHasMoreData(exp.length < expenseCount)
|
||||
setDataIndex(dataIndex + dataLen)
|
||||
setDataLen(Math.ceil(1.5 * dataLen))
|
||||
}
|
||||
const {
|
||||
data,
|
||||
isLoading: expensesAreLoading,
|
||||
fetchNextPage,
|
||||
} = trpc.groups.expenses.list.useInfiniteQuery(
|
||||
{ groupId, limit: PAGE_SIZE, filter: searchText },
|
||||
{ getNextPageParam: ({ nextCursor }) => nextCursor },
|
||||
)
|
||||
const expenses = data?.pages.flatMap((page) => page.expenses)
|
||||
const hasMore = data?.pages.at(-1)?.hasMore ?? false
|
||||
|
||||
setTimeout(() => setIsFetching(false), 500)
|
||||
}
|
||||
const isLoading = expensesAreLoading || !expenses || !group
|
||||
|
||||
if (inView && hasMoreData && !isFetching) fetchNextPage()
|
||||
}, [
|
||||
dataIndex,
|
||||
dataLen,
|
||||
expenseCount,
|
||||
expenses,
|
||||
groupId,
|
||||
hasMoreData,
|
||||
inView,
|
||||
isFetching,
|
||||
])
|
||||
useEffect(() => {
|
||||
if (inView && hasMore && !isLoading) fetchNextPage()
|
||||
}, [fetchNextPage, hasMore, inView, isLoading])
|
||||
|
||||
const groupedExpensesByDate = useMemo(
|
||||
() => getGroupedExpensesByDate(expenses),
|
||||
() => (expenses ? getGroupedExpensesByDate(expenses) : {}),
|
||||
[expenses],
|
||||
)
|
||||
|
||||
return expenses.length > 0 ? (
|
||||
if (isLoading) return <ExpensesLoading />
|
||||
|
||||
if (expenses.length === 0)
|
||||
return (
|
||||
<p className="px-6 text-sm py-6">
|
||||
{t('noExpenses')}{' '}
|
||||
<Button variant="link" asChild className="-m-4">
|
||||
<Link href={`/groups/${groupId}/expenses/create`}>
|
||||
{t('createFirst')}
|
||||
</Link>
|
||||
</Button>
|
||||
</p>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchBar onValueChange={(value) => setSearchText(value)} />
|
||||
{Object.values(EXPENSE_GROUPS).map((expenseGroup: string) => {
|
||||
let groupExpenses = groupedExpensesByDate[expenseGroup]
|
||||
if (!groupExpenses) return null
|
||||
|
||||
groupExpenses = groupExpenses.filter(({ title }) =>
|
||||
title.toLowerCase().includes(searchText.toLowerCase()),
|
||||
)
|
||||
|
||||
if (groupExpenses.length === 0) return null
|
||||
if (!groupExpenses || groupExpenses.length === 0) return null
|
||||
|
||||
return (
|
||||
<div key={expenseGroup}>
|
||||
@@ -152,44 +165,48 @@ export function ExpenseList({
|
||||
'text-muted-foreground text-xs pl-4 sm:pl-6 py-1 font-semibold sticky top-16 bg-white dark:bg-[#1b1917]'
|
||||
}
|
||||
>
|
||||
{expenseGroup}
|
||||
{t(`Groups.${expenseGroup}`)}
|
||||
</div>
|
||||
{groupExpenses.map((expense) => (
|
||||
<ExpenseCard
|
||||
key={expense.id}
|
||||
expense={expense}
|
||||
currency={currency}
|
||||
currency={getCurrencyFromGroup(group)}
|
||||
groupId={groupId}
|
||||
participantCount={group.participants.length}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{expenses.length < expenseCount &&
|
||||
[0, 1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="border-t flex justify-between items-center px-6 py-4 text-sm"
|
||||
ref={i === 0 ? ref : undefined}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
<Skeleton className="h-4 w-32 rounded-full" />
|
||||
</div>
|
||||
<div>
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{hasMore && <ExpensesLoading ref={loadingRef} />}
|
||||
</>
|
||||
) : (
|
||||
<p className="px-6 text-sm py-6">
|
||||
Your group doesn’t contain any expense yet.{' '}
|
||||
<Button variant="link" asChild className="-m-4">
|
||||
<Link href={`/groups/${groupId}/expenses/create`}>
|
||||
Create the first one
|
||||
</Link>
|
||||
</Button>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const ExpensesLoading = forwardRef<HTMLDivElement>((_, ref) => {
|
||||
return (
|
||||
<div ref={ref}>
|
||||
<Skeleton className="mx-4 sm:mx-6 mt-1 mb-2 h-3 w-32 rounded-full" />
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex justify-between items-start px-2 sm:px-6 py-4 text-sm gap-2"
|
||||
>
|
||||
<div className="flex-0 pl-2 pr-1">
|
||||
<Skeleton className="h-4 w-4 rounded-full" />
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col gap-2">
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
<Skeleton className="h-4 w-32 rounded-full" />
|
||||
</div>
|
||||
<div className="flex-0 flex flex-col gap-2 items-end mr-2 sm:mr-12">
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
<Skeleton className="h-4 w-20 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
ExpensesLoading.displayName = 'ExpensesLoading'
|
||||
|
||||
163
src/app/groups/[groupId]/expenses/export/csv/route.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { getCurrency } from '@/lib/currency'
|
||||
import { formatAmountAsDecimal, getCurrencyFromGroup } from '@/lib/utils'
|
||||
import { Parser } from '@json2csv/plainjs'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import contentDisposition from 'content-disposition'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
const splitModeLabel = {
|
||||
EVENLY: 'Evenly',
|
||||
BY_SHARES: 'Unevenly – By shares',
|
||||
BY_PERCENTAGE: 'Unevenly – By percentage',
|
||||
BY_AMOUNT: 'Unevenly – By amount',
|
||||
}
|
||||
|
||||
function formatDate(isoDateString: Date): string {
|
||||
const date = new Date(isoDateString)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0') // Months are zero-based
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}` // YYYY-MM-DD format
|
||||
}
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function GET(
|
||||
req: Request,
|
||||
{ params }: { params: Promise<{ groupId: string }> },
|
||||
) {
|
||||
const { groupId } = await params
|
||||
const group = await prisma.group.findUnique({
|
||||
where: { id: groupId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
currency: true,
|
||||
currencyCode: true,
|
||||
expenses: {
|
||||
select: {
|
||||
expenseDate: true,
|
||||
title: true,
|
||||
category: { select: { name: true } },
|
||||
amount: true,
|
||||
originalAmount: true,
|
||||
originalCurrency: true,
|
||||
conversionRate: true,
|
||||
paidById: true,
|
||||
paidFor: { select: { participantId: true, shares: true } },
|
||||
isReimbursement: true,
|
||||
splitMode: true,
|
||||
},
|
||||
},
|
||||
participants: { select: { id: true, name: true } },
|
||||
},
|
||||
})
|
||||
|
||||
if (!group) {
|
||||
return NextResponse.json({ error: 'Invalid group ID' }, { status: 404 })
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
CSV Columns:
|
||||
- Date: The date of the expense.
|
||||
- Description: A brief description of the expense.
|
||||
- Category: The category of the expense (e.g., Food, Travel, etc.).
|
||||
- Currency: The currency in which the expense is recorded.
|
||||
- Cost: The amount spent.
|
||||
- Original cost: The amount spent in the original currency.
|
||||
- Original currency: The currency the amount was originally spent in.
|
||||
- Conversion rate: The rate used to convert the amount.
|
||||
- Is Reimbursement: Whether the expense is a reimbursement or not.
|
||||
- Split mode: The method used to split the expense (e.g., Evenly, By shares, By percentage, By amount).
|
||||
- UserA, UserB: User-specific data or balances (e.g., amount owed or contributed by each user).
|
||||
|
||||
Example Table:
|
||||
+------------+------------------+----------+----------+----------+---------------+-------------------+-----------------+------------------+----------------------+--------+-----------+
|
||||
| Date | Description | Category | Currency | Cost | Original cost | Original currency | Conversion rate | Is reinbursement | Split mode | User A | User B |
|
||||
+------------+------------------+----------+----------+----------+---------------+-------------------+-----------------+------------------+----------------------+--------+-----------+
|
||||
| 2025-01-06 | Dinner with team | Food | INR | 5000 | | | | No | Evenly | 2500 | -2500 |
|
||||
+------------+------------------+----------+----------+----------+---------------+-------------------+-----------------+------------------+----------------------+--------+-----------+
|
||||
| 2025-02-07 | Plane tickets | Travel | INR | 97264.09 | 1000 | EUR | 97.2641 | No | Unevenly - By amount | -80000 | -17264.09 |
|
||||
+------------+------------------+----------+----------+----------+---------------+-------------------+-----------------+------------------+----------------------+--------+-----------+
|
||||
|
||||
*/
|
||||
|
||||
const fields = [
|
||||
{ label: 'Date', value: 'date' },
|
||||
{ label: 'Description', value: 'title' },
|
||||
{ label: 'Category', value: 'categoryName' },
|
||||
{ label: 'Currency', value: 'currency' },
|
||||
{ label: 'Cost', value: 'amount' },
|
||||
{ label: 'Original cost', value: 'originalAmount' },
|
||||
{ label: 'Original currency', value: 'originalCurrency' },
|
||||
{ label: 'Conversion rate', value: 'conversionRate' },
|
||||
{ label: 'Is Reimbursement', value: 'isReimbursement' },
|
||||
{ label: 'Split mode', value: 'splitMode' },
|
||||
...group.participants.map((participant) => ({
|
||||
label: participant.name,
|
||||
value: participant.name,
|
||||
})),
|
||||
]
|
||||
|
||||
const currency = getCurrencyFromGroup(group)
|
||||
|
||||
const expenses = group.expenses.map((expense) => ({
|
||||
date: formatDate(expense.expenseDate),
|
||||
title: expense.title,
|
||||
categoryName: expense.category?.name || '',
|
||||
currency: group.currencyCode ?? group.currency,
|
||||
amount: formatAmountAsDecimal(expense.amount, currency),
|
||||
originalAmount: expense.originalAmount
|
||||
? formatAmountAsDecimal(
|
||||
expense.originalAmount,
|
||||
getCurrency(expense.originalCurrency),
|
||||
)
|
||||
: null,
|
||||
originalCurrency: expense.originalCurrency,
|
||||
conversionRate: expense.conversionRate
|
||||
? expense.conversionRate.toString()
|
||||
: null,
|
||||
isReimbursement: expense.isReimbursement ? 'Yes' : 'No',
|
||||
splitMode: splitModeLabel[expense.splitMode],
|
||||
...Object.fromEntries(
|
||||
group.participants.map((participant) => {
|
||||
const { totalShares, participantShare } = expense.paidFor.reduce(
|
||||
(acc, { participantId, shares }) => {
|
||||
acc.totalShares += shares
|
||||
if (participantId === participant.id) {
|
||||
acc.participantShare = shares
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ totalShares: 0, participantShare: 0 },
|
||||
)
|
||||
|
||||
const isPaidByParticipant = expense.paidById === participant.id
|
||||
const participantAmountShare = +formatAmountAsDecimal(
|
||||
(expense.amount / totalShares) * participantShare,
|
||||
currency,
|
||||
)
|
||||
|
||||
return [
|
||||
participant.name,
|
||||
participantAmountShare * (isPaidByParticipant ? 1 : -1),
|
||||
]
|
||||
}),
|
||||
),
|
||||
}))
|
||||
|
||||
const json2csvParser = new Parser({ fields })
|
||||
const csv = json2csvParser.parse(expenses)
|
||||
|
||||
const date = new Date().toISOString().split('T')[0]
|
||||
const filename = `Spliit Export - ${group.name} - ${date}.csv`
|
||||
|
||||
// \uFEFF character is added at the beginning of the CSV content to ensure that it is interpreted as UTF-8 with BOM (Byte Order Mark), which helps some applications correctly interpret the encoding.
|
||||
return new NextResponse(`\uFEFF${csv}`, {
|
||||
headers: {
|
||||
'Content-Type': 'text/csv; charset=utf-8',
|
||||
'Content-Disposition': contentDisposition(filename),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -4,25 +4,33 @@ import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(
|
||||
req: Request,
|
||||
{ params: { groupId } }: { params: { groupId: string } },
|
||||
{ params }: { params: Promise<{ groupId: string }> },
|
||||
) {
|
||||
const { groupId } = await params
|
||||
const group = await prisma.group.findUnique({
|
||||
where: { id: groupId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
currency: true,
|
||||
currencyCode: true,
|
||||
expenses: {
|
||||
select: {
|
||||
createdAt: true,
|
||||
expenseDate: true,
|
||||
title: true,
|
||||
category: { select: { grouping: true, name: true } },
|
||||
amount: true,
|
||||
originalAmount: true,
|
||||
originalCurrency: true,
|
||||
conversionRate: true,
|
||||
paidById: true,
|
||||
paidFor: { select: { participantId: true, shares: true } },
|
||||
isReimbursement: true,
|
||||
splitMode: true,
|
||||
recurrenceRule: true,
|
||||
},
|
||||
orderBy: [{ expenseDate: 'asc' }, { createdAt: 'asc' }],
|
||||
},
|
||||
participants: { select: { id: true, name: true } },
|
||||
},
|
||||
@@ -31,7 +39,7 @@ export async function GET(
|
||||
return NextResponse.json({ error: 'Invalid group ID' }, { status: 404 })
|
||||
|
||||
const date = new Date().toISOString().split('T')[0]
|
||||
const filename = `Spliit Export - ${group.name} - ${date}`
|
||||
const filename = `Spliit Export - ${date}`
|
||||
return NextResponse.json(group, {
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
|
||||
65
src/app/groups/[groupId]/expenses/page.client.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
|
||||
import { ActiveUserModal } from '@/app/groups/[groupId]/expenses/active-user-modal'
|
||||
import { CreateFromReceiptButton } from '@/app/groups/[groupId]/expenses/create-from-receipt-button'
|
||||
import { ExpenseList } from '@/app/groups/[groupId]/expenses/expense-list'
|
||||
import ExportButton from '@/app/groups/[groupId]/export-button'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { Metadata } from 'next'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Expenses',
|
||||
}
|
||||
|
||||
export default function GroupExpensesPageClient({
|
||||
enableReceiptExtract,
|
||||
}: {
|
||||
enableReceiptExtract: boolean
|
||||
}) {
|
||||
const t = useTranslations('Expenses')
|
||||
const { groupId } = useCurrentGroup()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4 rounded-none -mx-4 border-x-0 sm:border-x sm:rounded-lg sm:mx-0">
|
||||
<div className="flex flex-1">
|
||||
<CardHeader className="flex-1 p-4 sm:p-6">
|
||||
<CardTitle>{t('title')}</CardTitle>
|
||||
<CardDescription>{t('description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardHeader className="p-4 sm:p-6 flex flex-row space-y-0 gap-2">
|
||||
<ExportButton groupId={groupId} />
|
||||
{enableReceiptExtract && <CreateFromReceiptButton />}
|
||||
<Button asChild size="icon">
|
||||
<Link
|
||||
href={`/groups/${groupId}/expenses/create`}
|
||||
title={t('create')}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-0 pt-2 pb-4 sm:pb-6 flex flex-col gap-4 relative">
|
||||
<ExpenseList />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<ActiveUserModal groupId={groupId} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,27 +1,6 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { ActiveUserModal } from '@/app/groups/[groupId]/expenses/active-user-modal'
|
||||
import { CreateFromReceiptButton } from '@/app/groups/[groupId]/expenses/create-from-receipt-button'
|
||||
import { ExpenseList } from '@/app/groups/[groupId]/expenses/expense-list'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import {
|
||||
getCategories,
|
||||
getGroupExpenseCount,
|
||||
getGroupExpenses,
|
||||
} from '@/lib/api'
|
||||
import GroupExpensesPageClient from '@/app/groups/[groupId]/expenses/page.client'
|
||||
import { env } from '@/lib/env'
|
||||
import { Download, Plus } from 'lucide-react'
|
||||
import { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
@@ -29,101 +8,10 @@ export const metadata: Metadata = {
|
||||
title: 'Expenses',
|
||||
}
|
||||
|
||||
export default async function GroupExpensesPage({
|
||||
params: { groupId },
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
const categories = await getCategories()
|
||||
|
||||
export default async function GroupExpensesPage() {
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4 rounded-none -mx-4 border-x-0 sm:border-x sm:rounded-lg sm:mx-0">
|
||||
<div className="flex flex-1">
|
||||
<CardHeader className="flex-1 p-4 sm:p-6">
|
||||
<CardTitle>Expenses</CardTitle>
|
||||
<CardDescription>
|
||||
Here are the expenses that you created for your group.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardHeader className="p-4 sm:p-6 flex flex-row space-y-0 gap-2">
|
||||
<Button variant="secondary" size="icon" asChild>
|
||||
<Link
|
||||
prefetch={false}
|
||||
href={`/groups/${groupId}/expenses/export/json`}
|
||||
target="_blank"
|
||||
title="Export to JSON"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
{env.NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT && (
|
||||
<CreateFromReceiptButton
|
||||
groupId={groupId}
|
||||
groupCurrency={group.currency}
|
||||
categories={categories}
|
||||
/>
|
||||
)}
|
||||
<Button asChild size="icon">
|
||||
<Link
|
||||
href={`/groups/${groupId}/expenses/create`}
|
||||
title="Create expense"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-0 pt-2 pb-4 sm:pb-6 flex flex-col gap-4 relative">
|
||||
<Suspense
|
||||
fallback={[0, 1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="border-t flex justify-between items-center px-6 py-4 text-sm"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
<Skeleton className="h-4 w-32 rounded-full" />
|
||||
</div>
|
||||
<div>
|
||||
<Skeleton className="h-4 w-16 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
>
|
||||
<Expenses group={group} />
|
||||
</Suspense>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<ActiveUserModal group={group} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
group: NonNullable<Awaited<ReturnType<typeof cached.getGroup>>>
|
||||
}
|
||||
|
||||
async function Expenses({ group }: Props) {
|
||||
const expenseCount = await getGroupExpenseCount(group.id)
|
||||
|
||||
const expenses = await getGroupExpenses(group.id, {
|
||||
offset: 0,
|
||||
length: 200,
|
||||
})
|
||||
|
||||
return (
|
||||
<ExpenseList
|
||||
expensesFirstPage={expenses}
|
||||
expenseCount={expenseCount}
|
||||
groupId={group.id}
|
||||
currency={group.currency}
|
||||
participants={group.participants}
|
||||
<GroupExpensesPageClient
|
||||
enableReceiptExtract={env.NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
53
src/app/groups/[groupId]/export-button.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Download, FileDown, FileJson } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function ExportButton({ groupId }: { groupId: string }) {
|
||||
const t = useTranslations('Expenses')
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button title={t('export')} variant="secondary" size="icon">
|
||||
<Download className="w-4 h-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
prefetch={false}
|
||||
href={`/groups/${groupId}/expenses/export/json`}
|
||||
target="_blank"
|
||||
title={t('exportJson')}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileJson className="w-4 h-4" />
|
||||
<p>{t('exportJson')}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
prefetch={false}
|
||||
href={`/groups/${groupId}/expenses/export/csv`}
|
||||
target="_blank"
|
||||
title={t('exportCsv')}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileDown className="w-4 h-4" />
|
||||
<p>{t('exportCsv')}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
30
src/app/groups/[groupId]/group-header.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import { GroupTabs } from '@/app/groups/[groupId]/group-tabs'
|
||||
import { ShareButton } from '@/app/groups/[groupId]/share-button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import Link from 'next/link'
|
||||
import { useCurrentGroup } from './current-group-context'
|
||||
|
||||
export const GroupHeader = () => {
|
||||
const { isLoading, groupId, group } = useCurrentGroup()
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-between gap-3">
|
||||
<h1 className="font-bold text-2xl">
|
||||
<Link href={`/groups/${groupId}`}>
|
||||
{isLoading ? (
|
||||
<Skeleton className="mt-1.5 mb-1.5 h-5 w-32" />
|
||||
) : (
|
||||
<div className="flex">{group.name}</div>
|
||||
)}
|
||||
</Link>
|
||||
</h1>
|
||||
|
||||
<div className="flex gap-2 justify-between">
|
||||
<GroupTabs groupId={groupId} />
|
||||
{group && <ShareButton group={group} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
|
||||
type Props = {
|
||||
@@ -7,6 +8,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export function GroupTabs({ groupId }: Props) {
|
||||
const t = useTranslations()
|
||||
const pathname = usePathname()
|
||||
const value =
|
||||
pathname.replace(/\/groups\/[^\/]+\/([^/]+).*/, '$1') || 'expenses'
|
||||
@@ -21,11 +23,12 @@ export function GroupTabs({ groupId }: Props) {
|
||||
}}
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="expenses">Expenses</TabsTrigger>
|
||||
<TabsTrigger value="balances">Balances</TabsTrigger>
|
||||
<TabsTrigger value="stats">Stats</TabsTrigger>
|
||||
<TabsTrigger value="activity">Activity</TabsTrigger>
|
||||
<TabsTrigger value="edit">Settings</TabsTrigger>
|
||||
<TabsTrigger value="expenses">{t('Expenses.title')}</TabsTrigger>
|
||||
<TabsTrigger value="balances">{t('Balances.title')}</TabsTrigger>
|
||||
<TabsTrigger value="information">{t('Information.title')}</TabsTrigger>
|
||||
<TabsTrigger value="stats">{t('Stats.title')}</TabsTrigger>
|
||||
<TabsTrigger value="activity">{t('Activity.title')}</TabsTrigger>
|
||||
<TabsTrigger value="edit">{t('Settings.title')}</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
)
|
||||
|
||||
52
src/app/groups/[groupId]/information/group-information.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Pencil } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
export default function GroupInformation({ groupId }: { groupId: string }) {
|
||||
const t = useTranslations('Information')
|
||||
const { isLoading, group } = useCurrentGroup()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex justify-between">
|
||||
<span>{t('title')}</span>
|
||||
<Button size="icon" asChild className="-mb-12">
|
||||
<Link href={`/groups/${groupId}/edit`}>
|
||||
<Pencil className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</CardTitle>
|
||||
<CardDescription className="mr-12">
|
||||
{t('description')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="prose prose-sm sm:prose-base max-w-full whitespace-break-spaces">
|
||||
{isLoading ? (
|
||||
<div className="py-1 flex flex-col gap-2">
|
||||
<Skeleton className="h-3 w-3/4" />
|
||||
<Skeleton className="h-3 w-1/2" />
|
||||
</div>
|
||||
) : group.information ? (
|
||||
<p className="text-foreground">{group.information}</p>
|
||||
) : (
|
||||
<p className="text-muted-foreground text-sm">{t('empty')}</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
15
src/app/groups/[groupId]/information/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import GroupInformation from '@/app/groups/[groupId]/information/group-information'
|
||||
import { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Group Information',
|
||||
}
|
||||
|
||||
export default async function InformationPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ groupId: string }>
|
||||
}) {
|
||||
const { groupId } = await params
|
||||
return <GroupInformation groupId={groupId} />
|
||||
}
|
||||
49
src/app/groups/[groupId]/layout.client.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { PropsWithChildren, useEffect } from 'react'
|
||||
import { CurrentGroupProvider } from './current-group-context'
|
||||
import { GroupHeader } from './group-header'
|
||||
import { SaveGroupLocally } from './save-recent-group'
|
||||
|
||||
export function GroupLayoutClient({
|
||||
groupId,
|
||||
children,
|
||||
}: PropsWithChildren<{ groupId: string }>) {
|
||||
const { data, isLoading } = trpc.groups.get.useQuery({ groupId })
|
||||
const t = useTranslations('Groups.NotFound')
|
||||
const { toast } = useToast()
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !data.group) {
|
||||
toast({
|
||||
description: t('text'),
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
}, [data])
|
||||
|
||||
const props =
|
||||
isLoading || !data?.group
|
||||
? { isLoading: true as const, groupId, group: undefined }
|
||||
: { isLoading: false as const, groupId, group: data.group }
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<CurrentGroupProvider {...props}>
|
||||
<GroupHeader />
|
||||
{children}
|
||||
</CurrentGroupProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<CurrentGroupProvider {...props}>
|
||||
<GroupHeader />
|
||||
{children}
|
||||
<SaveGroupLocally />
|
||||
</CurrentGroupProvider>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,16 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { GroupTabs } from '@/app/groups/[groupId]/group-tabs'
|
||||
import { SaveGroupLocally } from '@/app/groups/[groupId]/save-recent-group'
|
||||
import { ShareButton } from '@/app/groups/[groupId]/share-button'
|
||||
import { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { PropsWithChildren, Suspense } from 'react'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import { GroupLayoutClient } from './layout.client'
|
||||
|
||||
type Props = {
|
||||
params: {
|
||||
params: Promise<{
|
||||
groupId: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params: { groupId },
|
||||
}: Props): Promise<Metadata> {
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { groupId } = await params
|
||||
const group = await cached.getGroup(groupId)
|
||||
|
||||
return {
|
||||
@@ -28,29 +23,8 @@ export async function generateMetadata({
|
||||
|
||||
export default async function GroupLayout({
|
||||
children,
|
||||
params: { groupId },
|
||||
params,
|
||||
}: PropsWithChildren<Props>) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col justify-between gap-3">
|
||||
<h1 className="font-bold text-2xl">
|
||||
<Link href={`/groups/${groupId}`}>{group.name}</Link>
|
||||
</h1>
|
||||
|
||||
<div className="flex gap-2 justify-between">
|
||||
<Suspense>
|
||||
<GroupTabs groupId={groupId} />
|
||||
</Suspense>
|
||||
<ShareButton group={group} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
|
||||
<SaveGroupLocally group={{ id: group.id, name: group.name }} />
|
||||
</>
|
||||
)
|
||||
const { groupId } = await params
|
||||
return <GroupLayoutClient groupId={groupId}>{children}</GroupLayoutClient>
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export default async function GroupPage({
|
||||
params: { groupId },
|
||||
params,
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
params: Promise<{ groupId: string }>
|
||||
}) {
|
||||
const { groupId } = await params
|
||||
redirect(`/groups/${groupId}/expenses`)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Reimbursement } from '@/lib/balances'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { formatCurrency } from '@/lib/utils'
|
||||
import { Participant } from '@prisma/client'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
|
||||
type Props = {
|
||||
reimbursements: Reimbursement[]
|
||||
participants: Participant[]
|
||||
currency: string
|
||||
currency: Currency
|
||||
groupId: string
|
||||
}
|
||||
|
||||
@@ -17,33 +19,34 @@ export function ReimbursementList({
|
||||
currency,
|
||||
groupId,
|
||||
}: Props) {
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('Balances.Reimbursements')
|
||||
if (reimbursements.length === 0) {
|
||||
return (
|
||||
<p className="px-6 text-sm pb-6">
|
||||
It looks like your group doesn’t need any reimbursement 😁
|
||||
</p>
|
||||
)
|
||||
return <p className="text-sm pb-6">{t('noImbursements')}</p>
|
||||
}
|
||||
|
||||
const getParticipant = (id: string) => participants.find((p) => p.id === id)
|
||||
return (
|
||||
<div className="text-sm">
|
||||
{reimbursements.map((reimbursement, index) => (
|
||||
<div className="border-t px-6 py-4 flex justify-between" key={index}>
|
||||
<div className="py-4 flex justify-between" key={index}>
|
||||
<div className="flex flex-col gap-1 items-start sm:flex-row sm:items-baseline sm:gap-4">
|
||||
<div>
|
||||
<strong>{getParticipant(reimbursement.from)?.name}</strong> owes{' '}
|
||||
<strong>{getParticipant(reimbursement.to)?.name}</strong>
|
||||
{t.rich('owes', {
|
||||
from: getParticipant(reimbursement.from)?.name ?? '',
|
||||
to: getParticipant(reimbursement.to)?.name ?? '',
|
||||
strong: (chunks) => <strong>{chunks}</strong>,
|
||||
})}
|
||||
</div>
|
||||
<Button variant="link" asChild className="-mx-4 -my-3">
|
||||
<Link
|
||||
href={`/groups/${groupId}/expenses/create?reimbursement=yes&from=${reimbursement.from}&to=${reimbursement.to}&amount=${reimbursement.amount}`}
|
||||
>
|
||||
Mark as paid
|
||||
{t('markAsPaid')}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div>{formatCurrency(currency, reimbursement.amount)}</div>
|
||||
<div>{formatCurrency(currency, reimbursement.amount, locale)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
'use client'
|
||||
import {
|
||||
RecentGroup,
|
||||
saveRecentGroup,
|
||||
} from '@/app/groups/recent-groups-helpers'
|
||||
import { saveRecentGroup } from '@/app/groups/recent-groups-helpers'
|
||||
import { useEffect } from 'react'
|
||||
import { useCurrentGroup } from './current-group-context'
|
||||
|
||||
type Props = {
|
||||
group: RecentGroup
|
||||
}
|
||||
export function SaveGroupLocally() {
|
||||
const { group } = useCurrentGroup()
|
||||
|
||||
export function SaveGroupLocally({ group }: Props) {
|
||||
useEffect(() => {
|
||||
saveRecentGroup(group)
|
||||
if (group) saveRecentGroup({ id: group.id, name: group.name })
|
||||
}, [group])
|
||||
|
||||
return null
|
||||
|
||||
@@ -11,27 +11,26 @@ import {
|
||||
import { useBaseUrl } from '@/lib/hooks'
|
||||
import { Group } from '@prisma/client'
|
||||
import { Share } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
group: Group
|
||||
}
|
||||
|
||||
export function ShareButton({ group }: Props) {
|
||||
const t = useTranslations('Share')
|
||||
const baseUrl = useBaseUrl()
|
||||
const url = baseUrl && `${baseUrl}/groups/${group.id}/expenses?ref=share`
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button title="Share" size="icon" className="flex-shrink-0">
|
||||
<Button title={t('title')} size="icon" className="flex-shrink-0">
|
||||
<Share className="w-4 h-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="[&_p]:text-sm flex flex-col gap-3">
|
||||
<p>
|
||||
For other participants to see the group and add expenses, share its
|
||||
URL with them.
|
||||
</p>
|
||||
<p>{t('description')}</p>
|
||||
{url && (
|
||||
<div className="flex gap-2">
|
||||
<Input className="flex-1" defaultValue={url} readOnly />
|
||||
@@ -43,8 +42,7 @@ export function ShareButton({ group }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
<strong>Warning!</strong> Every person with the group URL will be able
|
||||
to see and edit expenses. Share with caution!
|
||||
<strong>{t('warning')}</strong> {t('warningHelp')}
|
||||
</p>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
27
src/app/groups/[groupId]/stats/page.client.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Totals } from '@/app/groups/[groupId]/stats/totals'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
export function TotalsPageClient() {
|
||||
const t = useTranslations('Stats')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('Totals.title')}</CardTitle>
|
||||
<CardDescription>{t('Totals.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col space-y-4">
|
||||
<Totals />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,49 +1,10 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { Totals } from '@/app/groups/[groupId]/stats/totals'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import { getTotalGroupSpending } from '@/lib/totals'
|
||||
import { TotalsPageClient } from '@/app/groups/[groupId]/stats/page.client'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Totals',
|
||||
}
|
||||
|
||||
export default async function TotalsPage({
|
||||
params: { groupId },
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
const expenses = await getGroupExpenses(groupId)
|
||||
const totalGroupSpendings = getTotalGroupSpending(expenses)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Totals</CardTitle>
|
||||
<CardDescription>
|
||||
Spending summary of the entire group.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col space-y-4">
|
||||
<Totals
|
||||
group={group}
|
||||
expenses={expenses}
|
||||
totalGroupSpendings={totalGroupSpendings}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
export default async function TotalsPage() {
|
||||
return <TotalsPageClient />
|
||||
}
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { formatCurrency } from '@/lib/utils'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
totalGroupSpendings: number
|
||||
currency: string
|
||||
currency: Currency
|
||||
}
|
||||
|
||||
export function TotalsGroupSpending({ totalGroupSpendings, currency }: Props) {
|
||||
const balance = totalGroupSpendings < 0 ? 'earnings' : 'spendings'
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('Stats.Totals')
|
||||
const balance = totalGroupSpendings < 0 ? 'groupEarnings' : 'groupSpendings'
|
||||
return (
|
||||
<div>
|
||||
<div className="text-muted-foreground">Total group {balance}</div>
|
||||
<div className="text-muted-foreground">{t(balance)}</div>
|
||||
<div className="text-lg">
|
||||
{formatCurrency(currency, Math.abs(totalGroupSpendings))}
|
||||
{formatCurrency(currency, Math.abs(totalGroupSpendings), locale)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,38 +1,28 @@
|
||||
'use client'
|
||||
import { getGroup, getGroupExpenses } from '@/lib/api'
|
||||
import { getTotalActiveUserShare } from '@/lib/totals'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { cn, formatCurrency } from '@/lib/utils'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||
expenses: NonNullable<Awaited<ReturnType<typeof getGroupExpenses>>>
|
||||
}
|
||||
|
||||
export function TotalsYourShare({ group, expenses }: Props) {
|
||||
const [activeUser, setActiveUser] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const activeUser = localStorage.getItem(`${group.id}-activeUser`)
|
||||
if (activeUser) setActiveUser(activeUser)
|
||||
}, [group, expenses])
|
||||
|
||||
const totalActiveUserShare =
|
||||
activeUser === '' || activeUser === 'None'
|
||||
? 0
|
||||
: getTotalActiveUserShare(activeUser, expenses)
|
||||
const currency = group.currency
|
||||
export function TotalsYourShare({
|
||||
totalParticipantShare = 0,
|
||||
currency,
|
||||
}: {
|
||||
totalParticipantShare?: number
|
||||
currency: Currency
|
||||
}) {
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('Stats.Totals')
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="text-muted-foreground">Your total share</div>
|
||||
<div className="text-muted-foreground">{t('yourShare')}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'text-lg',
|
||||
totalActiveUserShare < 0 ? 'text-green-600' : 'text-red-600',
|
||||
totalParticipantShare < 0 ? 'text-green-600' : 'text-red-600',
|
||||
)}
|
||||
>
|
||||
{formatCurrency(currency, Math.abs(totalActiveUserShare))}
|
||||
{formatCurrency(currency, Math.abs(totalParticipantShare), locale)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
'use client'
|
||||
import { getGroup, getGroupExpenses } from '@/lib/api'
|
||||
import { useActiveUser } from '@/lib/hooks'
|
||||
import { getTotalActiveUserPaidFor } from '@/lib/totals'
|
||||
import { Currency } from '@/lib/currency'
|
||||
import { cn, formatCurrency } from '@/lib/utils'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
|
||||
type Props = {
|
||||
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||
expenses: NonNullable<Awaited<ReturnType<typeof getGroupExpenses>>>
|
||||
}
|
||||
export function TotalsYourSpendings({
|
||||
totalParticipantSpendings = 0,
|
||||
currency,
|
||||
}: {
|
||||
totalParticipantSpendings?: number
|
||||
currency: Currency
|
||||
}) {
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('Stats.Totals')
|
||||
|
||||
export function TotalsYourSpendings({ group, expenses }: Props) {
|
||||
const activeUser = useActiveUser(group.id)
|
||||
|
||||
const totalYourSpendings =
|
||||
activeUser === '' || activeUser === 'None'
|
||||
? 0
|
||||
: getTotalActiveUserPaidFor(activeUser, expenses)
|
||||
const currency = group.currency
|
||||
const balance = totalYourSpendings < 0 ? 'earnings' : 'spendings'
|
||||
const balance =
|
||||
totalParticipantSpendings < 0 ? 'yourEarnings' : 'yourSpendings'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="text-muted-foreground">Your total {balance}</div>
|
||||
<div className="text-muted-foreground">{t(balance)}</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'text-lg',
|
||||
totalYourSpendings < 0 ? 'text-green-600' : 'text-red-600',
|
||||
totalParticipantSpendings < 0 ? 'text-green-600' : 'text-red-600',
|
||||
)}
|
||||
>
|
||||
{formatCurrency(currency, Math.abs(totalYourSpendings))}
|
||||
{formatCurrency(currency, Math.abs(totalParticipantSpendings), locale)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -2,31 +2,56 @@
|
||||
import { TotalsGroupSpending } from '@/app/groups/[groupId]/stats/totals-group-spending'
|
||||
import { TotalsYourShare } from '@/app/groups/[groupId]/stats/totals-your-share'
|
||||
import { TotalsYourSpendings } from '@/app/groups/[groupId]/stats/totals-your-spending'
|
||||
import { getGroup, getGroupExpenses } from '@/lib/api'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { useActiveUser } from '@/lib/hooks'
|
||||
import { getCurrencyFromGroup } from '@/lib/utils'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useCurrentGroup } from '../current-group-context'
|
||||
|
||||
export function Totals({
|
||||
group,
|
||||
expenses,
|
||||
totalGroupSpendings,
|
||||
}: {
|
||||
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||
expenses: NonNullable<Awaited<ReturnType<typeof getGroupExpenses>>>
|
||||
totalGroupSpendings: number
|
||||
}) {
|
||||
const activeUser = useActiveUser(group.id)
|
||||
console.log('activeUser', activeUser)
|
||||
export function Totals() {
|
||||
const { groupId, group } = useCurrentGroup()
|
||||
const activeUser = useActiveUser(groupId)
|
||||
|
||||
const participantId =
|
||||
activeUser && activeUser !== 'None' ? activeUser : undefined
|
||||
const { data } = trpc.groups.stats.get.useQuery({ groupId, participantId })
|
||||
|
||||
if (!data || !group)
|
||||
return (
|
||||
<div className="flex flex-col gap-7">
|
||||
{[0, 1, 2].map((index) => (
|
||||
<div key={index}>
|
||||
<Skeleton className="mt-1 h-3 w-48" />
|
||||
<Skeleton className="mt-3 h-4 w-20" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const {
|
||||
totalGroupSpendings,
|
||||
totalParticipantShare,
|
||||
totalParticipantSpendings,
|
||||
} = data
|
||||
|
||||
const currency = getCurrencyFromGroup(group)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TotalsGroupSpending
|
||||
totalGroupSpendings={totalGroupSpendings}
|
||||
currency={group.currency}
|
||||
currency={currency}
|
||||
/>
|
||||
{activeUser && activeUser !== 'None' && (
|
||||
{participantId && (
|
||||
<>
|
||||
<TotalsYourSpendings group={group} expenses={expenses} />
|
||||
<TotalsYourShare group={group} expenses={expenses} />
|
||||
<TotalsYourSpendings
|
||||
totalParticipantSpendings={totalParticipantSpendings}
|
||||
currency={currency}
|
||||
/>
|
||||
<TotalsYourShare
|
||||
totalParticipantShare={totalParticipantShare}
|
||||
currency={currency}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||