feat(i18n): Add Norwegian translation support and improve internationalization

- Add Norwegian (Norsk) language support with locale code normalization (no -> nb)
- Create Norwegian translation files (translations/nb/ and translations/no/)
- Fill empty Norwegian translation strings with English fallback values
- Add locale normalization for Flask-Babel compatibility (no -> nb mapping)
- Update context processor to correctly display 'Norsk' label instead of 'NB'

Translation improvements:
- Wrap all hardcoded strings in templates with _() translation function
- Add missing translations for setup, timer, tasks, invoices, and admin templates
- Ensure brandnames 'drytrix' and 'TimeTracker' remain untranslated across all languages
- Add new translation strings to all language files (en, de, nl, fr, it, fi, es, no, ar, he)
- Update translation files for: initial_setup, manual_entry, tasks/list, email_templates, etc.

Bug fixes:
- Add missing /api/summary/today endpoint for daily summary notifications
- Fix 'Response body already consumed' error in smart-notifications.js
- Improve translation compilation logging and error handling
- Add debug endpoint /debug/i18n for troubleshooting translation issues

Technical changes:
- Improve ensure_translations_compiled() with better logging
- Add locale normalization function for Norwegian locale handling
- Update context processor to reverse-map normalized locales for display
- Fix JavaScript fetch error handling to check response.ok before reading body
This commit is contained in:
Dries Peeters
2025-11-17 19:21:24 +01:00
parent 43324047b3
commit 3f73cb35c8
33 changed files with 23802 additions and 101 deletions

View File

@@ -340,17 +340,38 @@ def create_app(config=None):
if current_user and getattr(current_user, "is_authenticated", False):
pref = getattr(current_user, "preferred_language", None)
if pref:
return pref
# Normalize locale code (e.g., 'no' -> 'nb' for Norwegian)
return _normalize_locale(pref)
# 2) Session override (set-language route)
if "preferred_language" in session:
return session.get("preferred_language")
return _normalize_locale(session.get("preferred_language"))
# 3) Best match with Accept-Language
supported = list(app.config.get("LANGUAGES", {}).keys()) or ["en"]
return request.accept_languages.best_match(supported) or app.config.get(
matched = request.accept_languages.best_match(supported) or app.config.get(
"BABEL_DEFAULT_LOCALE", "en"
)
return _normalize_locale(matched)
except Exception:
return app.config.get("BABEL_DEFAULT_LOCALE", "en")
def _normalize_locale(locale_code):
"""Normalize locale codes for Flask-Babel compatibility.
Some locale codes need to be normalized:
- 'no' -> 'nb' (Norwegian Bokmål is the standard, but we'll try 'no' first)
"""
if not locale_code:
return 'en'
locale_code = locale_code.lower().strip()
# Try 'no' first - if translations don't exist, Flask-Babel will fall back
# If 'no' doesn't work, we can map to 'nb' as fallback
# For now, keep 'no' as-is since we have translations/nb/ directory
# The directory structure should match what Flask-Babel expects
if locale_code == 'no':
# Use 'nb' for Flask-Babel (standard Norwegian Bokmål locale)
# But ensure we have translations in both 'no' and 'nb' directories
return 'nb'
return locale_code
babel.init_app(
app,

View File

@@ -119,6 +119,7 @@ class Config:
'it': 'Italiano',
'fi': 'Suomi',
'es': 'Español',
'no': 'Norsk',
'ar': 'العربية',
'he': 'עברית',
}

View File

@@ -1513,6 +1513,35 @@ def dashboard_sparklines():
'month': hours_data # Same data for now
})
@api_bp.route('/api/summary/today')
@login_required
def summary_today():
"""Get today's time tracking summary for daily summary notification"""
from app.models import TimeEntry, Project
from datetime import datetime, timedelta
from sqlalchemy import func, distinct
today = datetime.utcnow().date()
# Get today's time entries for current user
entries = TimeEntry.query.filter(
TimeEntry.user_id == current_user.id,
func.date(TimeEntry.start_time) == today,
TimeEntry.end_time.isnot(None)
).all()
# Calculate total hours
total_hours = sum((entry.duration_hours or 0) for entry in entries)
# Count unique projects
project_ids = set(entry.project_id for entry in entries if entry.project_id)
project_count = len(project_ids)
return jsonify({
'hours': round(total_hours, 2),
'projects': project_count
})
@api_bp.route('/api/activity/timeline')
@login_required
def activity_timeline():

View File

@@ -128,6 +128,38 @@ def help():
"""Help page"""
return render_template('main/help.html')
@main_bp.route('/debug/i18n')
@login_required
def debug_i18n():
"""Debug endpoint to check i18n status (admin only)"""
from flask_login import current_user
if not current_user.is_admin:
return jsonify({'error': 'Admin only'}), 403
from flask_babel import get_locale
import os
locale = str(get_locale())
session_lang = session.get('preferred_language')
user_lang = getattr(current_user, 'preferred_language', None)
# Check if .mo file exists for current locale
base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
translations_dir = os.path.join(base_path, 'translations')
mo_path = os.path.join(translations_dir, locale, 'LC_MESSAGES', 'messages.mo')
po_path = os.path.join(translations_dir, locale, 'LC_MESSAGES', 'messages.po')
return jsonify({
'current_locale': locale,
'session_language': session_lang,
'user_language': user_lang,
'mo_file_exists': os.path.exists(mo_path),
'po_file_exists': os.path.exists(po_path),
'mo_path': mo_path,
'nb_mo_exists': os.path.exists(os.path.join(translations_dir, 'nb', 'LC_MESSAGES', 'messages.mo')),
'no_mo_exists': os.path.exists(os.path.join(translations_dir, 'no', 'LC_MESSAGES', 'messages.mo')),
})
@main_bp.route('/i18n/set-language', methods=['POST', 'GET'])
def set_language():
"""Set preferred UI language via session or user profile."""

View File

@@ -229,6 +229,14 @@ class SmartNotificationManager {
setInterval(async () => {
try {
const response = await fetch('/api/deadlines/upcoming');
// Check if response is OK before reading body
if (!response.ok) {
// If response is not OK, the error handler may have already consumed the body
// Just return early to avoid "Body has already been consumed" error
return;
}
const deadlines = await response.json();
deadlines.forEach(deadline => {
@@ -247,7 +255,10 @@ class SmartNotificationManager {
}
});
} catch (error) {
console.error('Error checking deadlines:', error);
// Only log if it's not a "body consumed" error (which is expected when response is not OK)
if (!error.message || !error.message.includes('already been consumed')) {
console.error('Error checking deadlines:', error);
}
}
}, 60 * 60 * 1000); // Check every hour
}
@@ -276,6 +287,14 @@ class SmartNotificationManager {
try {
const response = await fetch('/api/summary/today');
// Check if response is OK before reading body
if (!response.ok) {
// If response is not OK, the error handler may have already consumed the body
// Just return early to avoid "Body has already been consumed" error
return;
}
const summary = await response.json();
const hours = (summary && typeof summary.hours === 'number')
@@ -303,7 +322,10 @@ class SmartNotificationManager {
});
}
} catch (error) {
console.error('Error fetching daily summary:', error);
// Only log if it's not a "body consumed" error (which is expected when response is not OK)
if (!error.message || !error.message.includes('already been consumed')) {
console.error('Error fetching daily summary:', error);
}
}
}

View File

@@ -35,7 +35,7 @@
<div class="md:col-span-2">
<label class="flex items-center">
<input type="checkbox" name="is_default" class="rounded border-gray-300 text-primary focus:ring-primary">
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Set as default template</span>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">{{ _('Set as default template') }}</span>
</label>
</div>
</div>
@@ -43,19 +43,19 @@
<!-- Visual Editor Section -->
<div class="mt-6">
<div class="flex items-center justify-between mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">HTML Template *</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('HTML Template') }} *</label>
<div class="flex gap-2" id="variableButtons">
<button type="button" data-variable="invoice.invoice_number" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Invoice Number
{{ _('Invoice Number') }}
</button>
<button type="button" data-variable="company_name" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Company Name
{{ _('Company Name') }}
</button>
<button type="button" data-variable="custom_message" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Custom Message
{{ _('Custom Message') }}
</button>
<button type="button" id="showAllVariablesBtn" class="px-3 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-200 dark:hover:bg-gray-600">
More Variables
{{ _('More Variables') }}
</button>
</div>
</div>

View File

@@ -33,7 +33,7 @@
<div class="md:col-span-2">
<label class="flex items-center">
<input type="checkbox" name="is_default" {% if template.is_default %}checked{% endif %} class="rounded border-gray-300 text-primary focus:ring-primary">
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Set as default template</span>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">{{ _('Set as default template') }}</span>
</label>
</div>
</div>
@@ -41,19 +41,19 @@
<!-- Visual Editor Section -->
<div class="mt-6">
<div class="flex items-center justify-between mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">HTML Template *</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('HTML Template') }} *</label>
<div class="flex gap-2" id="variableButtons">
<button type="button" data-variable="invoice.invoice_number" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Invoice Number
{{ _('Invoice Number') }}
</button>
<button type="button" data-variable="company_name" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Company Name
{{ _('Company Name') }}
</button>
<button type="button" data-variable="custom_message" class="variable-btn px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800">
Custom Message
{{ _('Custom Message') }}
</button>
<button type="button" id="showAllVariablesBtn" class="px-3 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-200 dark:hover:bg-gray-600">
More Variables
{{ _('More Variables') }}
</button>
</div>
</div>

View File

@@ -53,7 +53,7 @@
{% if template.html %}
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow mb-6">
<h2 class="text-lg font-semibold mb-4">HTML Template</h2>
<h2 class="text-lg font-semibold mb-4">{{ _('HTML Template') }}</h2>
<pre class="bg-gray-100 dark:bg-gray-800 p-4 rounded-lg overflow-x-auto text-sm"><code>{{ template.html }}</code></pre>
</div>
{% endif %}

View File

@@ -81,7 +81,7 @@
<h2 class="text-lg font-semibold mb-4">Company Branding</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="company_name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Company Name</label>
<label for="company_name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Company Name') }}</label>
<input type="text" name="company_name" id="company_name" value="{{ settings.company_name }}" class="form-input">
</div>
<div>

View File

@@ -228,7 +228,7 @@
<div>
<label for="receipt_number" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Receipt/Invoice Number
{{ _('Receipt/Invoice Number') }}
</label>
<input type="text" name="receipt_number" id="receipt_number"
value="{{ expense.receipt_number if expense else '' }}"

View File

@@ -547,7 +547,7 @@ document.addEventListener('DOMContentLoaded', function() {
</select>
</div>
<div class="mb-4">
<label for="custom_message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Custom Message (Optional)</label>
<label for="custom_message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">{{ _('Custom Message') }} ({{ _('Optional') }})</label>
<textarea id="custom_message" name="custom_message" rows="4" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"></textarea>
</div>
<div class="flex justify-end space-x-3">

View File

@@ -69,7 +69,7 @@
</div>
<div>
<label for="search" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Search</label>
<input type="text" name="search" id="search" value="{{ request.args.get('search', '') }}" class="form-input" placeholder="Invoice number or client">
<input type="text" name="search" id="search" value="{{ request.args.get('search', '') }}" class="form-input" placeholder="{{ _('Invoice number or client') }}">
</div>
<div class="col-span-full flex justify-end">
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors">Filter</button>

View File

@@ -358,7 +358,7 @@
</select>
</div>
<div class="mb-4">
<label for="custom_message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Custom Message (Optional)</label>
<label for="custom_message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">{{ _('Custom Message') }} ({{ _('Optional') }})</label>
<textarea id="custom_message" name="custom_message" rows="4" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"></textarea>
</div>
<div class="flex justify-end space-x-3">

View File

@@ -152,7 +152,7 @@
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">Invoice Number</label>
<label class="block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">{{ _('Invoice Number') }}</label>
<a href="{{ url_for('invoices.view_invoice', invoice_id=payment.invoice_id) }}"
class="text-lg font-semibold text-primary hover:text-primary-dark">
{{ payment.invoice.invoice_number }}

View File

@@ -94,7 +94,7 @@
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Invoice Number</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{{ _('Invoice Number') }}</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Issue Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Amount</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th>

View File

@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ current_language_code or 'en' }}" dir="{{ 'rtl' if is_rtl else 'ltr' }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome - TimeTracker</title>
<title>{{ _('Welcome') }} - TimeTracker</title>
<link rel="stylesheet" href="{{ url_for('static', filename='dist/output.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='toast-notifications.css') }}">
<script>
@@ -23,34 +23,34 @@
<div class="text-center">
<img src="{{ url_for('static', filename='images/drytrix-logo.svg') }}" alt="logo" class="w-24 h-24 mx-auto">
<h1 class="text-3xl font-bold mt-4 text-primary">TimeTracker</h1>
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">Track time. Stay organized.</p>
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Track time. Stay organized.') }}</p>
<!-- Privacy Principles -->
<div class="mt-8 space-y-3 text-left">
<p class="text-sm font-semibold text-text-light dark:text-text-dark mb-3">🔒 Privacy First</p>
<p class="text-sm font-semibold text-text-light dark:text-text-dark mb-3">🔒 {{ _('Privacy First') }}</p>
<div class="flex items-start text-sm text-text-muted-light dark:text-text-muted-dark">
<svg class="h-5 w-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>Self-hosted on your server</span>
<span>{{ _('Self-hosted on your server') }}</span>
</div>
<div class="flex items-start text-sm text-text-muted-light dark:text-text-muted-dark">
<svg class="h-5 w-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>Anonymous telemetry (opt-in)</span>
<span>{{ _('Anonymous telemetry (opt-in)') }}</span>
</div>
<div class="flex items-start text-sm text-text-muted-light dark:text-text-muted-dark">
<svg class="h-5 w-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>No PII collected ever</span>
<span>{{ _('No PII collected ever') }}</span>
</div>
<div class="flex items-start text-sm text-text-muted-light dark:text-text-muted-dark">
<svg class="h-5 w-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>Open source & transparent</span>
<span>{{ _('Open source & transparent') }}</span>
</div>
</div>
</div>
@@ -58,31 +58,31 @@
<!-- Right side - Setup Form -->
<div class="p-8 overflow-y-auto max-h-[90vh]">
<h2 class="text-2xl font-bold tracking-tight">Welcome to TimeTracker</h2>
<p class="mt-2 text-sm text-text-muted-light dark:text-text-muted-dark">Let's get you set up in just a moment</p>
<h2 class="text-2xl font-bold tracking-tight">{{ _('Welcome to TimeTracker') }}</h2>
<p class="mt-2 text-sm text-text-muted-light dark:text-text-muted-dark">{{ _("Let's get you set up in just a moment") }}</p>
<form class="mt-6 space-y-6" method="POST" action="{{ url_for('setup.initial_setup') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<!-- Welcome Message -->
<div class="bg-primary/10 border border-primary/20 rounded-lg p-4">
<h3 class="text-sm font-semibold text-primary mb-2">🎉 Thank you for choosing TimeTracker!</h3>
<h3 class="text-sm font-semibold text-primary mb-2">🎉 {{ _('Thank you for choosing TimeTracker!') }}</h3>
<p class="text-sm text-text-muted-light dark:text-text-muted-dark">
Your data stays on your server, and you have complete control.
{{ _('Your data stays on your server, and you have complete control.') }}
</p>
</div>
<!-- Telemetry Opt-in Section -->
<div class="bg-background-light dark:bg-gray-700 border border-border-light dark:border-border-dark rounded-lg p-4">
<h3 class="text-base font-semibold mb-3">📊 Help Us Improve (Optional)</h3>
<h3 class="text-base font-semibold mb-3">📊 {{ _('Help Us Improve (Optional)') }}</h3>
<div class="mb-3">
<label class="flex items-start cursor-pointer">
<input type="checkbox" name="telemetry_enabled" class="mt-1 h-4 w-4 text-primary border-border-light dark:border-border-dark rounded focus:ring-primary">
<span class="ml-3 text-sm">
<span class="font-medium">Enable anonymous telemetry</span>
<span class="font-medium">{{ _('Enable anonymous telemetry') }}</span>
<span class="block text-text-muted-light dark:text-text-muted-dark mt-1">
Help us understand usage patterns to improve TimeTracker
{{ _('Help us understand usage patterns to improve TimeTracker') }}
</span>
</span>
</label>
@@ -91,34 +91,34 @@
<!-- Collapsible details -->
<details class="text-sm mt-3">
<summary class="cursor-pointer font-medium text-primary hover:underline">
What data is collected?
{{ _('What data is collected?') }}
</summary>
<div class="mt-3 space-y-3 pl-4 border-l-2 border-primary/30">
<div>
<p class="font-semibold text-green-600 dark:text-green-400">✓ What we collect:</p>
<p class="font-semibold text-green-600 dark:text-green-400">{{ _('What we collect:') }}</p>
<ul class="list-disc list-inside space-y-1 ml-2 text-text-muted-light dark:text-text-muted-dark">
<li>Anonymous installation fingerprint (hashed)</li>
<li>Application version & platform info</li>
<li>Feature usage statistics</li>
<li>Internal numeric IDs only</li>
<li>{{ _('Anonymous installation fingerprint (hashed)') }}</li>
<li>{{ _('Application version & platform info') }}</li>
<li>{{ _('Feature usage statistics') }}</li>
<li>{{ _('Internal numeric IDs only') }}</li>
</ul>
</div>
<div>
<p class="font-semibold text-red-600 dark:text-red-400">✗ What we DON'T collect:</p>
<p class="font-semibold text-red-600 dark:text-red-400">{{ _("What we DON'T collect:") }}</p>
<ul class="list-disc list-inside space-y-1 ml-2 text-text-muted-light dark:text-text-muted-dark">
<li>No usernames or emails</li>
<li>No project names or descriptions</li>
<li>No time entry data or notes</li>
<li>No client or business data</li>
<li>No IP addresses or PII</li>
<li>{{ _('No usernames or emails') }}</li>
<li>{{ _('No project names or descriptions') }}</li>
<li>{{ _('No time entry data or notes') }}</li>
<li>{{ _('No client or business data') }}</li>
<li>{{ _('No IP addresses or PII') }}</li>
</ul>
</div>
<div class="bg-primary/10 rounded p-3 text-xs">
<p class="text-text-light dark:text-text-dark">
<strong>Why?</strong> Anonymous usage data helps us prioritize features and fix issues.
You can change this anytime in <strong>Admin → Settings</strong> (Privacy & Analytics section).
<strong>{{ _('Why?') }}</strong> {{ _('Anonymous usage data helps us prioritize features and fix issues.') }}
{{ _('You can change this anytime in') }} <strong>{{ _('Admin → Settings') }}</strong> ({{ _('Privacy & Analytics section') }}).
</p>
</div>
</div>
@@ -128,11 +128,11 @@
<!-- Submit Button -->
<button type="submit" class="btn btn-primary w-full">
<i class="fa-solid fa-check mr-2"></i>
Complete Setup & Continue
{{ _('Complete Setup & Continue') }}
</button>
<div class="text-xs text-center text-text-muted-light dark:text-text-muted-dark">
<p>By continuing, you agree to use TimeTracker under the <a href="https://www.gnu.org/licenses/gpl-3.0.html" class="text-primary hover:underline" target="_blank">GPL-3.0 License</a></p>
<p>{{ _('By continuing, you agree to use TimeTracker under the') }} <a href="https://www.gnu.org/licenses/gpl-3.0.html" class="text-primary hover:underline" target="_blank">{{ _('GPL-3.0 License') }}</a></p>
</div>
</form>
</div>

View File

@@ -296,17 +296,17 @@
<div id="bulkProjectDialog" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-card-light dark:bg-card-dark rounded-lg shadow-xl max-w-md w-full mx-4">
<div class="p-6">
<h3 class="text-lg font-semibold mb-4">Move Selected Tasks to Project</h3>
<label for="bulkProjectSelect" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Select Project</label>
<h3 class="text-lg font-semibold mb-4">{{ _('Move Selected Tasks to Project') }}</h3>
<label for="bulkProjectSelect" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">{{ _('Select Project') }}</label>
<select id="bulkProjectSelect" class="form-input w-full mb-4">
<option value="">-- Select Project --</option>
<option value="">-- {{ _('Select Project') }} --</option>
{% for project in projects %}
<option value="{{ project.id }}">{{ project.name }}</option>
{% endfor %}
</select>
<div class="flex justify-end gap-2">
<button type="button" onclick="closeBulkProjectDialog()" class="px-4 py-2 text-sm bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600">Cancel</button>
<button type="button" onclick="submitBulkProject()" class="px-4 py-2 text-sm bg-primary text-white rounded-lg hover:bg-primary/90">Move Tasks</button>
<button type="button" onclick="closeBulkProjectDialog()" class="px-4 py-2 text-sm bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600">{{ _('Cancel') }}</button>
<button type="button" onclick="submitBulkProject()" class="px-4 py-2 text-sm bg-primary text-white rounded-lg hover:bg-primary/90">{{ _('Move Tasks') }}</button>
</div>
</div>
</div>

View File

@@ -3,14 +3,14 @@
{% block content %}
{% set breadcrumbs = [
{'text': 'Time Tracking'},
{'text': 'Log Time' if not is_duplicate else 'Duplicate Entry'}
{'text': _('Time Tracking')},
{'text': _('Log Time') if not is_duplicate else _('Duplicate Entry')}
] %}
{{ page_header(
icon_class='fas fa-clock',
title_text='Duplicate Time Entry' if is_duplicate else 'Log Time Manually',
subtitle_text='Create a copy of a previous entry with new times' if is_duplicate else 'Create a new time entry',
title_text=_('Duplicate Time Entry') if is_duplicate else _('Log Time Manually'),
subtitle_text=_('Create a copy of a previous entry with new times') if is_duplicate else _('Create a new time entry'),
breadcrumbs=breadcrumbs,
actions_html=None
) }}
@@ -21,10 +21,10 @@
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mt-1"></i>
<div>
<p class="text-sm text-blue-800 dark:text-blue-200">
<strong>Duplicating entry:</strong> {{ original_entry.project.name }}{% if original_entry.task %} - {{ original_entry.task.name }}{% endif %}
<strong>{{ _('Duplicating entry:') }}</strong> {{ original_entry.project.name }}{% if original_entry.task %} - {{ original_entry.task.name }}{% endif %}
</p>
<p class="text-xs text-blue-600 dark:text-blue-300 mt-1">
Original: {{ original_entry.start_time|user_datetime('%Y-%m-%d %H:%M') }} to {{ original_entry.end_time|user_datetime('%Y-%m-%d %H:%M') if original_entry.end_time else 'N/A' }} ({{ original_entry.duration_formatted }})
{{ _('Original:') }} {{ original_entry.start_time|user_datetime('%Y-%m-%d %H:%M') }} {{ _('to') }} {{ original_entry.end_time|user_datetime('%Y-%m-%d %H:%M') if original_entry.end_time else _('N/A') }} ({{ original_entry.duration_formatted }})
</p>
</div>
</div>
@@ -37,66 +37,66 @@
<div class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="project_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Project</label>
<label for="project_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Project') }}</label>
<select name="project_id" id="project_id" required class="form-input">
<option value="">Select a project</option>
<option value="">{{ _('Select a project') }}</option>
{% for project in projects %}
<option value="{{ project.id }}" {% if selected_project_id == project.id %}selected{% endif %}>{{ project.name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="task_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Task (optional)</label>
<label for="task_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Task (optional)') }}</label>
<select name="task_id" id="task_id" class="form-input" data-selected-task-id="{{ selected_task_id or '' }}" {% if not selected_project_id %}disabled{% endif %}>
<option value="">No task</option>
<option value="">{{ _('No task') }}</option>
</select>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">Tasks load after selecting a project</p>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Tasks load after selecting a project') }}</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Start Date</label>
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Start Date') }}</label>
<input type="date" name="start_date" id="start_date" required class="form-input">
</div>
<div>
<label for="start_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Start Time</label>
<label for="start_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Start Time') }}</label>
<input type="time" name="start_time" id="start_time" required class="form-input">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">End Date</label>
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('End Date') }}</label>
<input type="date" name="end_date" id="end_date" required class="form-input">
</div>
<div>
<label for="end_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300">End Time</label>
<label for="end_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('End Time') }}</label>
<input type="time" name="end_time" id="end_time" required class="form-input">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="md:col-span-2">
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Notes</label>
<textarea name="notes" id="notes" rows="4" class="form-input" placeholder="What did you work on?">{{ prefill_notes or '' }}</textarea>
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Notes') }}</label>
<textarea name="notes" id="notes" rows="4" class="form-input" placeholder="{{ _('What did you work on?') }}">{{ prefill_notes or '' }}</textarea>
</div>
<div class="space-y-4">
<div>
<label for="tags" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Tags</label>
<input type="text" name="tags" id="tags" class="form-input" placeholder="tag1, tag2" value="{{ prefill_tags or '' }}">
<label for="tags" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Tags') }}</label>
<input type="text" name="tags" id="tags" class="form-input" placeholder="{{ _('tag1, tag2') }}" value="{{ prefill_tags or '' }}">
</div>
<div class="flex items-center gap-3">
<input type="checkbox" id="billable" name="billable" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if prefill_billable is not defined or prefill_billable %}checked{% endif %}>
<label for="billable" class="text-sm text-gray-700 dark:text-gray-300">Billable</label>
<label for="billable" class="text-sm text-gray-700 dark:text-gray-300">{{ _('Billable') }}</label>
</div>
</div>
</div>
</div>
<div class="mt-8 border-t border-border-light dark:border-border-dark pt-6 flex justify-end gap-3">
<button type="reset" class="px-4 py-2 rounded-lg border border-border-light dark:border-border-dark text-text-light dark:text-text-dark hover:bg-background-light dark:hover:bg-background-dark">Clear</button>
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg">Log Time</button>
<button type="reset" class="px-4 py-2 rounded-lg border border-border-light dark:border-border-dark text-text-light dark:text-text-dark hover:bg-background-light dark:hover:bg-background-dark">{{ _('Clear') }}</button>
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg">{{ _('Log Time') }}</button>
</div>
</form>
</div>
@@ -119,19 +119,21 @@ document.addEventListener('DOMContentLoaded', async function(){
// Dynamic task loading when a project is chosen
const projectSelect = document.getElementById('project_id');
const taskSelect = document.getElementById('task_id');
const noTaskText = '{{ _('No task') }}';
const failedToLoadTasksText = '{{ _('Failed to load tasks') }}';
async function loadTasks(projectId){
if (!taskSelect) return;
if (!projectId){
taskSelect.innerHTML = '<option value="">No task</option>';
taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
taskSelect.disabled = true;
return;
}
try{
const resp = await fetch(`/api/tasks?project_id=${projectId}`);
if (!resp.ok) throw new Error('Failed to load tasks');
if (!resp.ok) throw new Error(failedToLoadTasksText);
const data = await resp.json();
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
taskSelect.innerHTML = '<option value="">No task</option>';
taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
tasks.forEach(t => {
const opt = document.createElement('option');
opt.value = String(t.id);
@@ -146,7 +148,7 @@ document.addEventListener('DOMContentLoaded', async function(){
}
taskSelect.disabled = false;
}catch(e){
taskSelect.innerHTML = '<option value="">No task</option>';
taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
taskSelect.disabled = true;
}
}

View File

@@ -121,24 +121,24 @@
<div>
<label for="task_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Task <span class="text-text-muted-light dark:text-text-muted-dark">(Optional)</span>
{{ _('Task') }} <span class="text-text-muted-light dark:text-text-muted-dark">({{ _('Optional') }})</span>
</label>
<select name="task_id" id="task_id" class="form-input w-full">
<option value="">No task</option>
<option value="">{{ _('No task') }}</option>
</select>
</div>
<div>
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Notes <span class="text-text-muted-light dark:text-text-muted-dark">(Optional)</span>
{{ _('Notes') }} <span class="text-text-muted-light dark:text-text-muted-dark">({{ _('Optional') }})</span>
</label>
<textarea name="notes" id="notes" rows="3" class="form-input w-full" placeholder="Add notes about what you're working on..."></textarea>
<textarea name="notes" id="notes" rows="3" class="form-input w-full" placeholder="{{ _('Add notes about what you\'re working on...') }}"></textarea>
</div>
{% if templates %}
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Or use a template
{{ _('Or use a template') }}
</label>
<div class="space-y-2">
{% for template in templates %}

View File

@@ -80,8 +80,15 @@ def register_context_processors(app):
current_locale = 'en'
# Normalize to short code for comparisons (e.g., 'en' from 'en_US')
short_locale = (current_locale.split('_', 1)[0] if current_locale else 'en')
# Reverse-map normalized locale codes back to config keys for label lookup
# 'nb' (used by Flask-Babel) should map back to 'no' (used in LANGUAGES config)
display_locale = short_locale
if short_locale == 'nb':
display_locale = 'no'
available_languages = current_app.config.get('LANGUAGES', {}) or {}
current_language_label = available_languages.get(short_locale, short_locale.upper())
current_language_label = available_languages.get(display_locale, short_locale.upper())
# Check if current language is RTL
rtl_languages = current_app.config.get('RTL_LANGUAGES', set())
@@ -94,7 +101,7 @@ def register_context_processors(app):
'timezone_offset': get_timezone_offset_for_timezone(timezone_name),
'user_timezone': user_timezone,
'current_locale': current_locale,
'current_language_code': short_locale,
'current_language_code': display_locale, # Use display locale (e.g., 'no' not 'nb')
'current_language_label': current_language_label,
'is_rtl': is_rtl,
'available_languages': available_languages,

View File

@@ -28,7 +28,15 @@ def compile_po_to_mo(po_path: str, mo_path: str) -> bool:
with open(mo_path, 'wb') as mo_file:
write_mo(mo_file, catalog)
return True
except Exception:
except ImportError:
# Babel not installed - this is expected in some environments
# The app will fall back to English if .mo files don't exist
return False
except Exception as e:
# Log the actual error for debugging
import logging
logger = logging.getLogger('timetracker')
logger.warning(f"Error compiling {po_path}: {e}")
return False
@@ -52,9 +60,23 @@ def ensure_translations_compiled(translations_dir: str) -> None:
po_path = os.path.join(lang_dir, 'messages.po')
mo_path = os.path.join(lang_dir, 'messages.mo')
if os.path.exists(po_path) and _needs_compile(po_path, mo_path):
compile_po_to_mo(po_path, mo_path)
except Exception:
import logging
logger = logging.getLogger('timetracker')
logger.info(f"Compiling translations for {lang}...")
success = compile_po_to_mo(po_path, mo_path)
if success:
if os.path.exists(mo_path):
logger.info(f"Successfully compiled translations for {lang}")
else:
logger.warning(f"Compilation reported success but {mo_path} not found")
else:
# Log failure - this is important for debugging
logger.warning(f"Failed to compile translations for {lang} - translations may not work. Check if Babel is installed.")
except Exception as e:
# Non-fatal; i18n will fall back to msgid if mo missing
import logging
logger = logging.getLogger('timetracker')
logger.warning(f"Error compiling translations: {e}")
pass

View File

@@ -7,7 +7,7 @@ from setuptools import setup, find_packages
setup(
name='timetracker',
version='3.10.1',
version='3.10.2',
packages=find_packages(),
include_package_data=True,
install_requires=[

View File

@@ -294,7 +294,7 @@ msgid "Company Logo"
msgstr "شعار الشركة"
msgid "DryTrix Logo"
msgstr "شعار DryTrix"
msgstr "DryTrix Logo"
msgid "TimeTracker"
msgstr "TimeTracker"
@@ -479,3 +479,226 @@ msgstr "%(app)s هو تطبيق ويب لتتبع الوقت مصمم للاست
msgid "Learn more about "
msgstr "تعرف على المزيد حول "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "الخصوصية أولاً"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "مستضاف ذاتياً على خادمك"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "قياس مجهول (اختياري)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "لا يتم جمع بيانات شخصية أبداً"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "مفتوح المصدر وشفاف"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "مرحباً بك في TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "دعنا نعدك في لحظة"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "شكراً لاختيارك TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "تبقى بياناتك على خادمك ولديك سيطرة كاملة."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "ساعدنا على التحسين (اختياري)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "تفعيل القياس المجهول"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "ساعدنا على فهم أنماط الاستخدام لتحسين TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "ما هي البيانات التي يتم جمعها؟"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "ما نجمع:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "بصمة تثبيت مجهولة (مشفرة)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "إصدار التطبيق ومعلومات المنصة"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "إحصائيات استخدام الميزات"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "معرفات رقمية داخلية فقط"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "ما لا نجمع:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "لا أسماء مستخدمين أو بريد إلكتروني"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "لا أسماء مشاريع أو أوصاف"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "لا بيانات إدخال الوقت أو ملاحظات"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "لا بيانات عميل أو أعمال"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "لا عناوين IP أو بيانات شخصية"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "لماذا؟"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "بيانات الاستخدام المجهولة تساعدنا على تحديد أولويات الميزات وإصلاح المشكلات."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "يمكنك تغيير هذا في أي وقت في"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "المسؤول → الإعدادات"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "قسم الخصوصية والتحليلات"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "إكمال الإعداد والمتابعة"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "بالمتابعة، أنت توافق على استخدام TimeTracker بموجب"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "رخصة GPL-3.0"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "تكرار إدخال الوقت"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "تسجيل الوقت يدوياً"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "إنشاء نسخة من إدخال سابق بأوقات جديدة"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "إنشاء إدخال وقت جديد"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "تكرار الإدخال:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "الأصلي:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "إلى"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "غير متاح"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "على ماذا عملت؟"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "فشل تحميل المهام"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "نقل المهام المحددة إلى المشروع"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "نقل المهام"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "أضف ملاحظات حول ما تعمل عليه..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "تعيين كقالب افتراضي"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "قالب HTML"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "رقم الفاتورة"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "اسم الشركة"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "رسالة مخصصة"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "المزيد من المتغيرات"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "رقم الفاتورة أو العميل"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "رقم الإيصال/الفاتورة"

View File

@@ -478,3 +478,226 @@ msgstr "%(app)s ist eine webbasierte Zeiterfassungsanwendung für die interne Nu
msgid "Learn more about "
msgstr "Erfahren Sie mehr über "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Datenschutz zuerst"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Selbst gehostet auf Ihrem Server"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Anonyme Telemetrie (Opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Keine personenbezogenen Daten werden jemals gesammelt"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Open Source & transparent"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Willkommen bei TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Lassen Sie uns Sie in einem Moment einrichten"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Vielen Dank, dass Sie TimeTracker gewählt haben!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Ihre Daten bleiben auf Ihrem Server und Sie haben die vollständige Kontrolle."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Helfen Sie uns zu verbessern (Optional)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Anonyme Telemetrie aktivieren"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Helfen Sie uns, Nutzungsmuster zu verstehen, um TimeTracker zu verbessern"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "Welche Daten werden gesammelt?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Was wir sammeln:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Anonymer Installations-Fingerabdruck (gehasht)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Anwendungsversion & Plattforminformationen"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Funktionsnutzungsstatistiken"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Nur interne numerische IDs"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Was wir NICHT sammeln:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Keine Benutzernamen oder E-Mails"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Keine Projektnamen oder Beschreibungen"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Keine Zeiteintragsdaten oder Notizen"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Keine Kunden- oder Geschäftsdaten"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Keine IP-Adressen oder personenbezogene Daten"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Warum?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Anonyme Nutzungsdaten helfen uns, Funktionen zu priorisieren und Probleme zu beheben."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "Sie können dies jederzeit ändern in"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Einstellungen"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Bereich Datenschutz & Analyse"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Einrichtung abschließen & fortfahren"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "Durch Fortfahren stimmen Sie zu, TimeTracker unter der"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "GPL-3.0-Lizenz"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Zeiteintrag duplizieren"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Zeit manuell erfassen"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Eine Kopie eines vorherigen Eintrags mit neuen Zeiten erstellen"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Neuen Zeiteintrag erstellen"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Eintrag duplizieren:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Original:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "bis"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N/V"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "Woran haben Sie gearbeitet?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Aufgaben konnten nicht geladen werden"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Ausgewählte Aufgaben zum Projekt verschieben"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Aufgaben verschieben"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Notizen hinzufügen, woran Sie arbeiten..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Als Standardvorlage festlegen"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "HTML-Vorlage"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Rechnungsnummer"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Firmenname"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Benutzerdefinierte Nachricht"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Weitere Variablen"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Rechnungsnummer oder Kunde"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Beleg-/Rechnungsnummer"

View File

@@ -10555,6 +10555,229 @@ msgstr ""
#~ "tracking application designed for internal "
#~ "use within organizations."
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Privacy First"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Self-hosted on your server"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Anonymous telemetry (opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "No PII collected ever"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Open source & transparent"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Welcome to TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Let's get you set up in just a moment"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Thank you for choosing TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Your data stays on your server, and you have complete control."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Help Us Improve (Optional)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Enable anonymous telemetry"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Help us understand usage patterns to improve TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "What data is collected?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "What we collect:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Anonymous installation fingerprint (hashed)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Application version & platform info"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Feature usage statistics"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Internal numeric IDs only"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "What we DON'T collect:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "No usernames or emails"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "No project names or descriptions"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "No time entry data or notes"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "No client or business data"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "No IP addresses or PII"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Why?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Anonymous usage data helps us prioritize features and fix issues."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "You can change this anytime in"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Settings"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Privacy & Analytics section"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Complete Setup & Continue"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "By continuing, you agree to use TimeTracker under the"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "GPL-3.0 License"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Duplicate Time Entry"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Log Time Manually"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Create a copy of a previous entry with new times"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Create a new time entry"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Duplicating entry:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Original:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "to"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N/A"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "What did you work on?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Failed to load tasks"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Move Selected Tasks to Project"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Move Tasks"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Add notes about what you're working on..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Set as default template"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "HTML Template"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Invoice Number"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Company Name"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Custom Message"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "More Variables"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Invoice number or client"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Receipt/Invoice Number"
#~ msgid "Learn more about "
#~ msgstr "Learn more about "

View File

@@ -294,7 +294,7 @@ msgid "Company Logo"
msgstr "Logo de la Empresa"
msgid "DryTrix Logo"
msgstr "Logo de DryTrix"
msgstr "DryTrix Logo"
msgid "TimeTracker"
msgstr "TimeTracker"
@@ -479,3 +479,226 @@ msgstr "%(app)s es una aplicación web de seguimiento del tiempo diseñada para
msgid "Learn more about "
msgstr "Más información sobre "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Privacidad primero"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Alojado en su servidor"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Telemetría anónima (opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Nunca se recopilan datos personales"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Código abierto y transparente"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Bienvenido a TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Configuremos en un momento"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "¡Gracias por elegir TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Sus datos permanecen en su servidor y tiene control total."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Ayúdenos a mejorar (Opcional)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Habilitar telemetría anónima"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Ayúdenos a entender los patrones de uso para mejorar TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "¿Qué datos se recopilan?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Lo que recopilamos:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Huella digital de instalación anónima (hash)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Versión de la aplicación e información de la plataforma"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Estadísticas de uso de funciones"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Solo ID numéricos internos"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Lo que NO recopilamos:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Sin nombres de usuario ni correos electrónicos"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Sin nombres o descripciones de proyectos"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Sin datos de entrada de tiempo ni notas"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Sin datos de clientes o comerciales"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Sin direcciones IP ni datos personales"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "¿Por qué?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Los datos de uso anónimos nos ayudan a priorizar funciones y corregir problemas."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "Puede cambiar esto en cualquier momento en"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Configuración"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Sección de Privacidad y Análisis"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Completar configuración y continuar"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "Al continuar, acepta usar TimeTracker bajo la"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "Licencia GPL-3.0"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Duplicar entrada de tiempo"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Registrar tiempo manualmente"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Crear una copia de una entrada anterior con nuevos horarios"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Crear una nueva entrada de tiempo"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Duplicando entrada:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Original:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "a"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N/D"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "¿En qué trabajaste?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Error al cargar tareas"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Mover tareas seleccionadas al proyecto"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Mover tareas"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Agregar notas sobre en qué estás trabajando..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Establecer como plantilla predeterminada"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "Plantilla HTML"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Número de factura"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Nombre de la empresa"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Mensaje personalizado"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Más variables"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Número de factura o cliente"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Número de recibo/factura"

View File

@@ -478,3 +478,226 @@ msgstr "%(app)s on verkkopohjainen ajanseurantasovellus, joka on suunniteltu org
msgid "Learn more about "
msgstr "Lue lisää "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Yksityisyys ensin"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Oma palvelin"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Anonyymi telemetria (valinnainen)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Henkilötietoja ei koskaan kerätä"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Avoin lähdekoodi ja läpinäkyvä"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Tervetuloa TimeTrackeriin"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Asetetaan sinut hetkessä käyttöön"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Kiitos, että valitsit TimeTrackerin!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Tietosi pysyvät palvelimellasi ja sinulla on täysi kontrolli."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Auta meitä parantamaan (Valinnainen)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Ota anonyymi telemetria käyttöön"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Auta meitä ymmärtämään käyttömalleja parantaaksemme TimeTrackeria"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "Mitä tietoja kerätään?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Mitä keräämme:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Anonyymi asennuksen sormenjälki (hash)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Sovelluksen versio ja alustatiedot"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Ominaisuuksien käyttötilastot"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Vain sisäiset numeeriset ID:t"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Mitä emme kerää:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Ei käyttäjänimiä tai sähköposteja"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Ei projektinimiä tai kuvauksia"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Ei ajan kirjaustietoja tai muistiinpanoja"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Ei asiakas- tai liiketoimintatietoja"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Ei IP-osoitteita tai henkilötietoja"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Miksi?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Anonyymi käyttötieto auttaa meitä priorisoimaan ominaisuuksia ja korjaamaan ongelmia."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "Voit muuttaa tämän milloin tahansa"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Asetukset"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Yksityisyys ja analytiikka -osio"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Viimeistele asennus ja jatka"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "Jatkamalla hyväksyt TimeTrackerin käytön"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "GPL-3.0-lisenssi"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Monista ajan kirjaus"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Kirjaa aika manuaalisesti"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Luo kopio aiemmasta merkinnästä uusilla ajoilla"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Luo uusi ajan kirjaus"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Monistetaan merkintää:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Alkuperäinen:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr ""
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "E/T"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "Mitä teit?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Tehtävien lataaminen epäonnistui"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Siirrä valitut tehtävät projektiin"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Siirrä tehtävät"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Lisää muistiinpanoja siitä, mitä teet..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Aseta oletusmalliksi"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "HTML-malli"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Laskunumero"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Yrityksen nimi"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Mukautettu viesti"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Lisää muuttujia"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Laskunumero tai asiakas"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Kuitti-/Laskunumero"

View File

@@ -478,3 +478,226 @@ msgstr "%(app)s est une application web de suivi du temps conçue pour un usage
msgid "Learn more about "
msgstr "En savoir plus sur "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Confidentialité d'abord"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Auto-hébergé sur votre serveur"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Télémétrie anonyme (opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Aucune donnée personnelle collectée"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Open source et transparent"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Bienvenue sur TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Configurons-vous en un instant"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Merci d'avoir choisi TimeTracker !"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Vos données restent sur votre serveur et vous avez un contrôle total."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Aidez-nous à nous améliorer (Optionnel)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Activer la télémétrie anonyme"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Aidez-nous à comprendre les modèles d'utilisation pour améliorer TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "Quelles données sont collectées ?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Ce que nous collectons :"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Empreinte d'installation anonyme (hachée)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Version de l'application et informations sur la plateforme"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Statistiques d'utilisation des fonctionnalités"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "ID numériques internes uniquement"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Ce que nous ne collectons PAS :"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Aucun nom d'utilisateur ou e-mail"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Aucun nom ou description de projet"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Aucune donnée d'entrée de temps ou note"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Aucune donnée client ou commerciale"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Aucune adresse IP ou donnée personnelle"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Pourquoi ?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Les données d'utilisation anonymes nous aident à prioriser les fonctionnalités et à corriger les problèmes."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "Vous pouvez modifier cela à tout moment dans"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Paramètres"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Section Confidentialité et Analyses"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Terminer la configuration et continuer"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "En continuant, vous acceptez d'utiliser TimeTracker sous la"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "Licence GPL-3.0"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Dupliquer l'entrée de temps"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Enregistrer le temps manuellement"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Créer une copie d'une entrée précédente avec de nouveaux horaires"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Créer une nouvelle entrée de temps"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Duplication de l'entrée :"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Original :"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "à"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N/D"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "Sur quoi avez-vous travaillé ?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Échec du chargement des tâches"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Déplacer les tâches sélectionnées vers le projet"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Déplacer les tâches"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Ajouter des notes sur ce sur quoi vous travaillez..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Définir comme modèle par défaut"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "Modèle HTML"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Numéro de facture"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Nom de l'entreprise"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Message personnalisé"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Plus de variables"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Numéro de facture ou client"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Numéro de reçu/facture"

View File

@@ -294,7 +294,7 @@ msgid "Company Logo"
msgstr "לוגו החברה"
msgid "DryTrix Logo"
msgstr "לוגו DryTrix"
msgstr "DryTrix Logo"
msgid "TimeTracker"
msgstr "TimeTracker"
@@ -479,3 +479,226 @@ msgstr "%(app)s הוא אפליקציית מעקב זמן מבוססת אינט
msgid "Learn more about "
msgstr "למד עוד על "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "פרטיות קודם כל"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "מארח עצמי בשרת שלך"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "טלמטריה אנונימית (אופציונלי)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "אין איסוף מידע אישי לעולם"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "קוד פתוח ושקוף"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "ברוך הבא ל-TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "בואו נגדיר אותך בעוד רגע"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "תודה שבחרת ב-TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "הנתונים שלך נשארים בשרת שלך ויש לך שליטה מלאה."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "עזור לנו להשתפר (אופציונלי)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "הפעל טלמטריה אנונימית"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "עזור לנו להבין דפוסי שימוש כדי לשפר את TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "אילו נתונים נאספים?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "מה שאנחנו אוספים:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "טביעת אצבע של התקנה אנונימית (מוצפנת)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "גרסת האפליקציה ומידע על הפלטפורמה"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "סטטיסטיקות שימוש בתכונות"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "רק מזהה מספרי פנימי"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "מה שאנחנו לא אוספים:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "אין שמות משתמש או אימיילים"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "אין שמות פרויקטים או תיאורים"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "אין נתוני רישום זמן או הערות"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "אין נתוני לקוח או עסק"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "אין כתובות IP או מידע אישי"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "למה?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "נתוני שימוש אנונימיים עוזרים לנו לתעדף תכונות ולתקן בעיות."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "אתה יכול לשנות זאת בכל עת ב"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "מנהל → הגדרות"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "סעיף פרטיות ואנליטיקה"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "השלם הגדרה והמשך"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "בהמשך, אתה מסכים להשתמש ב-TimeTracker תחת"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "רישיון GPL-3.0"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "שכפל רישום זמן"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "רשום זמן ידנית"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "צור עותק של רישום קודם עם זמנים חדשים"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "צור רישום זמן חדש"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "משכפל רישום:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "מקורי:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "עד"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "לא זמין"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "על מה עבדת?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "נכשל בטעינת משימות"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "העבר משימות נבחרות לפרויקט"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "העבר משימות"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "הוסף הערות על מה שאתה עובד..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "הגדר כתבנית ברירת מחדל"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "תבנית HTML"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "מספר חשבונית"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "שם החברה"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "הודעה מותאמת אישית"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "משתנים נוספים"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "מספר חשבונית או לקוח"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "מספר קבלה/חשבונית"

View File

@@ -478,3 +478,226 @@ msgstr "%(app)s è un'applicazione web per la registrazione del tempo progettata
msgid "Learn more about "
msgstr "Scopri di più su "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Privacy prima di tutto"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Self-hosted sul tuo server"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Telemetria anonima (opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Nessun dato personale raccolto mai"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Open source e trasparente"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Benvenuto su TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Configuriamoti in un attimo"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Grazie per aver scelto TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "I tuoi dati rimangono sul tuo server e hai il controllo completo."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Aiutaci a migliorare (Opzionale)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Abilita telemetria anonima"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Aiutaci a capire i modelli di utilizzo per migliorare TimeTracker"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "Quali dati vengono raccolti?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Cosa raccogliamo:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Impronta digitale di installazione anonima (hash)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Versione applicazione e informazioni sulla piattaforma"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Statistiche di utilizzo delle funzionalità"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Solo ID numerici interni"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Cosa NON raccogliamo:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Nessun nome utente o email"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Nessun nome o descrizione del progetto"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Nessun dato di registrazione del tempo o note"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Nessun dato cliente o aziendale"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Nessun indirizzo IP o dato personale"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Perché?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "I dati di utilizzo anonimi ci aiutano a dare priorità alle funzionalità e a risolvere i problemi."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "Puoi modificarlo in qualsiasi momento in"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Impostazioni"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Sezione Privacy e Analisi"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Completa configurazione e continua"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "Continuando, accetti di utilizzare TimeTracker sotto la"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "Licenza GPL-3.0"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Duplica registrazione tempo"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Registra tempo manualmente"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Crea una copia di una registrazione precedente con nuovi orari"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Crea una nuova registrazione tempo"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Duplicazione registrazione:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Originale:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "a"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N/D"
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "Su cosa hai lavorato?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Impossibile caricare le attività"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Sposta attività selezionate al progetto"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Sposta attività"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Aggiungi note su ciò su cui stai lavorando..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Imposta come modello predefinito"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "Modello HTML"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Numero fattura"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Nome azienda"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Messaggio personalizzato"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Più variabili"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Numero fattura o cliente"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Numero ricevuta/fattura"

File diff suppressed because it is too large Load Diff

View File

@@ -478,3 +478,226 @@ msgstr "%(app)s is een webgebaseerde tijdregistratie-applicatie ontworpen voor i
msgid "Learn more about "
msgstr "Meer informatie over "
#: app/templates/setup/initial_setup.html:30
msgid "Privacy First"
msgstr "Privacy eerst"
#: app/templates/setup/initial_setup.html:35
msgid "Self-hosted on your server"
msgstr "Zelf gehost op uw server"
#: app/templates/setup/initial_setup.html:41
msgid "Anonymous telemetry (opt-in)"
msgstr "Anonieme telemetrie (opt-in)"
#: app/templates/setup/initial_setup.html:47
msgid "No PII collected ever"
msgstr "Geen persoonsgegevens worden ooit verzameld"
#: app/templates/setup/initial_setup.html:53
msgid "Open source & transparent"
msgstr "Open source & transparant"
#: app/templates/setup/initial_setup.html:61
msgid "Welcome to TimeTracker"
msgstr "Welkom bij TimeTracker"
#: app/templates/setup/initial_setup.html:62
msgid "Let's get you set up in just a moment"
msgstr "Laten we u in een ogenblik instellen"
#: app/templates/setup/initial_setup.html:69
msgid "Thank you for choosing TimeTracker!"
msgstr "Bedankt voor het kiezen van TimeTracker!"
#: app/templates/setup/initial_setup.html:71
msgid "Your data stays on your server, and you have complete control."
msgstr "Uw gegevens blijven op uw server en u heeft volledige controle."
#: app/templates/setup/initial_setup.html:77
msgid "Help Us Improve (Optional)"
msgstr "Help ons te verbeteren (Optioneel)"
#: app/templates/setup/initial_setup.html:83
msgid "Enable anonymous telemetry"
msgstr "Anonieme telemetrie inschakelen"
#: app/templates/setup/initial_setup.html:85
msgid "Help us understand usage patterns to improve TimeTracker"
msgstr "Help ons gebruikspatronen te begrijpen om TimeTracker te verbeteren"
#: app/templates/setup/initial_setup.html:94
msgid "What data is collected?"
msgstr "Welke gegevens worden verzameld?"
#: app/templates/setup/initial_setup.html:98
msgid "What we collect:"
msgstr "Wat we verzamelen:"
#: app/templates/setup/initial_setup.html:100
msgid "Anonymous installation fingerprint (hashed)"
msgstr "Anonieme installatiefingerafdruk (gehasht)"
#: app/templates/setup/initial_setup.html:101
msgid "Application version & platform info"
msgstr "Applicatieversie & platforminfo"
#: app/templates/setup/initial_setup.html:102
msgid "Feature usage statistics"
msgstr "Functiegebruikstatistieken"
#: app/templates/setup/initial_setup.html:103
msgid "Internal numeric IDs only"
msgstr "Alleen interne numerieke ID's"
#: app/templates/setup/initial_setup.html:108
msgid "What we DON'T collect:"
msgstr "Wat we NIET verzamelen:"
#: app/templates/setup/initial_setup.html:110
msgid "No usernames or emails"
msgstr "Geen gebruikersnamen of e-mails"
#: app/templates/setup/initial_setup.html:111
msgid "No project names or descriptions"
msgstr "Geen projectnamen of beschrijvingen"
#: app/templates/setup/initial_setup.html:112
msgid "No time entry data or notes"
msgstr "Geen tijdregistratiegegevens of notities"
#: app/templates/setup/initial_setup.html:113
msgid "No client or business data"
msgstr "Geen klant- of bedrijfsgegevens"
#: app/templates/setup/initial_setup.html:114
msgid "No IP addresses or PII"
msgstr "Geen IP-adressen of persoonsgegevens"
#: app/templates/setup/initial_setup.html:120
msgid "Why?"
msgstr "Waarom?"
#: app/templates/setup/initial_setup.html:120
msgid "Anonymous usage data helps us prioritize features and fix issues."
msgstr "Anonieme gebruiksgegevens helpen ons functies te prioriteren en problemen op te lossen."
#: app/templates/setup/initial_setup.html:121
msgid "You can change this anytime in"
msgstr "U kunt dit altijd wijzigen in"
#: app/templates/setup/initial_setup.html:121
msgid "Admin → Settings"
msgstr "Admin → Instellingen"
#: app/templates/setup/initial_setup.html:121
msgid "Privacy & Analytics section"
msgstr "Sectie Privacy & Analytics"
#: app/templates/setup/initial_setup.html:131
msgid "Complete Setup & Continue"
msgstr "Setup voltooien & doorgaan"
#: app/templates/setup/initial_setup.html:135
msgid "By continuing, you agree to use TimeTracker under the"
msgstr "Door door te gaan, stemt u ermee in TimeTracker te gebruiken onder de"
#: app/templates/setup/initial_setup.html:135
msgid "GPL-3.0 License"
msgstr "GPL-3.0-licentie"
#: app/templates/timer/manual_entry.html:12
msgid "Duplicate Time Entry"
msgstr "Tijdregistratie dupliceren"
#: app/templates/timer/manual_entry.html:12
msgid "Log Time Manually"
msgstr "Tijd handmatig registreren"
#: app/templates/timer/manual_entry.html:13
msgid "Create a copy of a previous entry with new times"
msgstr "Maak een kopie van een eerdere registratie met nieuwe tijden"
#: app/templates/timer/manual_entry.html:13
msgid "Create a new time entry"
msgstr "Nieuwe tijdregistratie maken"
#: app/templates/timer/manual_entry.html:24
msgid "Duplicating entry:"
msgstr "Registratie dupliceren:"
#: app/templates/timer/manual_entry.html:27
msgid "Original:"
msgstr "Origineel:"
#: app/templates/timer/manual_entry.html:27
msgid "to"
msgstr "tot"
#: app/templates/timer/manual_entry.html:27
msgid "N/A"
msgstr "N.v.t."
#: app/templates/timer/manual_entry.html:82
msgid "What did you work on?"
msgstr "Waar heb je aan gewerkt?"
#: app/templates/timer/manual_entry.html:87
msgid "tag1, tag2"
msgstr "tag1, tag2"
#: app/templates/timer/manual_entry.html:131
msgid "Failed to load tasks"
msgstr "Taken konden niet worden geladen"
#: app/templates/tasks/list.html:299
msgid "Move Selected Tasks to Project"
msgstr "Geselecteerde taken naar project verplaatsen"
#: app/templates/tasks/list.html:309
msgid "Move Tasks"
msgstr "Taken verplaatsen"
#: app/templates/timer/timer_page.html:135
msgid "Add notes about what you're working on..."
msgstr "Notities toevoegen over waar je aan werkt..."
#: app/templates/admin/email_templates/edit.html:36
#: app/templates/admin/email_templates/create.html:38
msgid "Set as default template"
msgstr "Instellen als standaardsjabloon"
#: app/templates/admin/email_templates/edit.html:44
#: app/templates/admin/email_templates/create.html:46
#: app/templates/admin/email_templates/view.html:56
msgid "HTML Template"
msgstr "HTML-sjabloon"
#: app/templates/admin/email_templates/edit.html:47
#: app/templates/admin/email_templates/create.html:49
msgid "Invoice Number"
msgstr "Factuurnummer"
#: app/templates/admin/email_templates/edit.html:50
#: app/templates/admin/email_templates/create.html:52
msgid "Company Name"
msgstr "Bedrijfsnaam"
#: app/templates/admin/email_templates/edit.html:53
#: app/templates/admin/email_templates/create.html:55
msgid "Custom Message"
msgstr "Aangepast bericht"
#: app/templates/admin/email_templates/edit.html:56
#: app/templates/admin/email_templates/create.html:58
msgid "More Variables"
msgstr "Meer variabelen"
#: app/templates/invoices/list.html:72
msgid "Invoice number or client"
msgstr "Factuurnummer of klant"
#: app/templates/expenses/form.html:231
msgid "Receipt/Invoice Number"
msgstr "Bon-/Factuurnummer"

File diff suppressed because it is too large Load Diff