Write your representatives, EU version

Compare changes

Choose any two refs to compare.

+3647 -420
+3
.gitignore
··· 14 *.sqlite3 15 db.sqlite3 16 17 # Shapefile components (use fetch_wahlkreis_data to regenerate GeoJSON) 18 *.shp 19 *.shx
··· 14 *.sqlite3 15 db.sqlite3 16 17 + # Compiled translation files (generated from .po) 18 + *.mo 19 + 20 # Shapefile components (use fetch_wahlkreis_data to regenerate GeoJSON) 21 *.shp 22 *.shx
+41
README.md
··· 7 4. Download constituency boundaries: `uv run python manage.py fetch_wahlkreis_data` (required for accurate address-based matching). 8 5. Launch the dev server with `uv run python manage.py runserver` and visit http://localhost:8000/. 9 10 ## Architecture 11 - **Frameworks**: Django 5.2 / Python 3.13 managed with `uv`. The project root holds dependency metadata; all Django code lives in `website/` (settings in `writethem/`, app logic in `letters/`). 12 - **Domain models**: `letters/models.py` defines parliaments, terms, constituencies, representatives, committees, letters, signatures, identity verification, and moderation reports. Relationships reflect multi-level mandates (EU/Federal/State) and committee membership.
··· 7 4. Download constituency boundaries: `uv run python manage.py fetch_wahlkreis_data` (required for accurate address-based matching). 8 5. Launch the dev server with `uv run python manage.py runserver` and visit http://localhost:8000/. 9 10 + ## Internationalization 11 + 12 + WriteThem.eu supports German (default) and English. 13 + 14 + ### Using the Site 15 + 16 + - Visit `/de/` for German interface 17 + - Visit `/en/` for English interface 18 + - Use the language switcher in the header to toggle languages 19 + - Language preference is saved in a cookie 20 + 21 + ### For Developers 22 + 23 + **Translation workflow:** 24 + 25 + 1. Wrap new UI strings with translation functions: 26 + - Templates: `{% trans "Text" %}` or `{% blocktrans %}` 27 + - Python: `gettext()` or `gettext_lazy()` 28 + 29 + 2. Extract strings to .po files: 30 + ```bash 31 + cd website 32 + uv run python manage.py makemessages -l de -l en 33 + ``` 34 + 35 + 3. Translate strings in `.po` files: 36 + - Edit `locale/de/LC_MESSAGES/django.po` (German translations) 37 + - Edit `locale/en/LC_MESSAGES/django.po` (English, mostly identity translations) 38 + 39 + 4. Compile translations: 40 + ```bash 41 + uv run python manage.py compilemessages 42 + ``` 43 + 44 + 5. Check translation completeness: 45 + ```bash 46 + uv run python manage.py check_translations 47 + ``` 48 + 49 + **Important:** All code, comments, and translation keys should be in English. Only .po files contain actual translations. 50 + 51 ## Architecture 52 - **Frameworks**: Django 5.2 / Python 3.13 managed with `uv`. The project root holds dependency metadata; all Django code lives in `website/` (settings in `writethem/`, app logic in `letters/`). 53 - **Domain models**: `letters/models.py` defines parliaments, terms, constituencies, representatives, committees, letters, signatures, identity verification, and moderation reports. Relationships reflect multi-level mandates (EU/Federal/State) and committee membership.
+9
docs/matching-algorithm.md
··· 81 python manage.py test letters.tests.test_topic_mapping 82 python manage.py test letters.tests.test_constituency_suggestions 83 ```
··· 81 python manage.py test letters.tests.test_topic_mapping 82 python manage.py test letters.tests.test_constituency_suggestions 83 ``` 84 + 85 + ## Internationalization 86 + 87 + The constituency matching system works identically in both German and English: 88 + 89 + - Addresses can be entered in German format (standard use case) 90 + - UI language (German/English) does not affect geocoding or matching logic 91 + - Representative names, constituency names, and geographic data remain in original German 92 + - All user-facing labels and messages are translated
+1127
docs/plans/2025-10-14-i18n-implementation.md
···
··· 1 + # German + English Internationalization Implementation Plan 2 + 3 + > **For Claude:** Use `${SUPERPOWERS_SKILLS_ROOT}/skills/collaboration/executing-plans/SKILL.md` to implement this plan task-by-task. 4 + 5 + **Goal:** Implement full bilingual support (German + English) using Django's built-in i18n system. 6 + 7 + **Architecture:** Configure Django i18n settings, wrap all UI strings in gettext translation functions, create German and English locale files (.po), add language switcher component, and configure URL patterns with language prefixes. 8 + 9 + **Tech Stack:** Django 5.2 i18n framework, gettext, .po/.mo translation files, LocaleMiddleware 10 + 11 + --- 12 + 13 + ## Task 1: Configure Django i18n Settings 14 + 15 + **Files:** 16 + - Modify: `website/writethem/settings.py:104-146` 17 + 18 + **Step 1: Write the failing test** 19 + 20 + Create: `website/letters/tests/test_i18n.py` 21 + 22 + ```python 23 + # ABOUTME: Tests for internationalization configuration and functionality. 24 + # ABOUTME: Verifies language switching, URL prefixes, and translation completeness. 25 + 26 + from django.test import TestCase 27 + from django.conf import settings 28 + 29 + 30 + class I18nConfigurationTests(TestCase): 31 + def test_i18n_enabled(self): 32 + """Test that USE_I18N is enabled.""" 33 + self.assertTrue(settings.USE_I18N) 34 + 35 + def test_supported_languages(self): 36 + """Test that German and English are configured.""" 37 + language_codes = [code for code, name in settings.LANGUAGES] 38 + self.assertIn('de', language_codes) 39 + self.assertIn('en', language_codes) 40 + 41 + def test_locale_paths_configured(self): 42 + """Test that LOCALE_PATHS is set.""" 43 + self.assertTrue(len(settings.LOCALE_PATHS) > 0) 44 + ``` 45 + 46 + **Step 2: Run test to verify it fails** 47 + 48 + Run: `uv run python manage.py test letters.tests.test_i18n::I18nConfigurationTests -v` 49 + Expected: FAIL with assertion errors (USE_I18N=False, LANGUAGES not configured, LOCALE_PATHS not set) 50 + 51 + **Step 3: Update settings.py** 52 + 53 + In `website/writethem/settings.py`, replace lines 104-114: 54 + 55 + ```python 56 + # Internationalization 57 + # https://docs.djangoproject.com/en/5.2/topics/i18n/ 58 + 59 + LANGUAGE_CODE = 'de' 60 + LANGUAGES = [ 61 + ('de', 'Deutsch'), 62 + ('en', 'English'), 63 + ] 64 + 65 + TIME_ZONE = 'Europe/Berlin' 66 + 67 + USE_I18N = True 68 + USE_L10N = True 69 + 70 + USE_TZ = True 71 + 72 + # Locale paths - where Django looks for .po files 73 + LOCALE_PATHS = [ 74 + BASE_DIR / 'locale', 75 + ] 76 + ``` 77 + 78 + **Step 4: Add LocaleMiddleware** 79 + 80 + In `website/writethem/settings.py`, modify MIDDLEWARE list (lines 43-51): 81 + 82 + ```python 83 + MIDDLEWARE = [ 84 + 'django.middleware.security.SecurityMiddleware', 85 + 'django.contrib.sessions.middleware.SessionMiddleware', 86 + 'django.middleware.locale.LocaleMiddleware', # NEW - handles language detection 87 + 'django.middleware.common.CommonMiddleware', 88 + 'django.middleware.csrf.CsrfViewMiddleware', 89 + 'django.contrib.auth.middleware.AuthenticationMiddleware', 90 + 'django.contrib.messages.middleware.MessageMiddleware', 91 + 'django.middleware.clickjacking.XFrameOptionsMiddleware', 92 + ] 93 + ``` 94 + 95 + **Step 5: Run test to verify it passes** 96 + 97 + Run: `uv run python manage.py test letters.tests.test_i18n::I18nConfigurationTests -v` 98 + Expected: PASS (3 tests) 99 + 100 + **Step 6: Commit** 101 + 102 + ```bash 103 + git add website/writethem/settings.py website/letters/tests/test_i18n.py 104 + git commit -m "feat: configure Django i18n with German and English support" 105 + ``` 106 + 107 + --- 108 + 109 + ## Task 2: Configure URL Patterns with Language Prefixes 110 + 111 + **Files:** 112 + - Modify: `website/writethem/urls.py` 113 + 114 + **Step 1: Write the failing test** 115 + 116 + Add to `website/letters/tests/test_i18n.py`: 117 + 118 + ```python 119 + class I18nURLTests(TestCase): 120 + def test_german_url_prefix_works(self): 121 + """Test that German URL prefix is accessible.""" 122 + response = self.client.get('/de/') 123 + self.assertEqual(response.status_code, 200) 124 + 125 + def test_english_url_prefix_works(self): 126 + """Test that English URL prefix is accessible.""" 127 + response = self.client.get('/en/') 128 + self.assertEqual(response.status_code, 200) 129 + 130 + def test_set_language_endpoint_exists(self): 131 + """Test that language switcher endpoint exists.""" 132 + from django.urls import reverse 133 + url = reverse('set_language') 134 + self.assertEqual(url, '/i18n/setlang/') 135 + ``` 136 + 137 + **Step 2: Run test to verify it fails** 138 + 139 + Run: `uv run python manage.py test letters.tests.test_i18n::I18nURLTests -v` 140 + Expected: FAIL (URLs not configured with language prefixes) 141 + 142 + **Step 3: Update URLs configuration** 143 + 144 + Replace entire contents of `website/writethem/urls.py`: 145 + 146 + ```python 147 + """ 148 + URL configuration for writethem project. 149 + 150 + The `urlpatterns` list routes URLs to views. For more information please see: 151 + https://docs.djangoproject.com/en/5.2/topics/http/urls/ 152 + Examples: 153 + Function views 154 + 1. Add an import: from my_app import views 155 + 2. Add a URL to urlpatterns: path('', views.home, name='home') 156 + Class-based views 157 + 1. Add an import: from other_app.views import Home 158 + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 159 + Including another URLconf 160 + 1. Import the include() function: from django.urls import include, path 161 + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 162 + """ 163 + from django.contrib import admin 164 + from django.urls import path, include 165 + from django.conf import settings 166 + from django.conf.urls.static import static 167 + from django.conf.urls.i18n import i18n_patterns 168 + from django.views.i18n import set_language 169 + 170 + urlpatterns = [ 171 + # Language switcher endpoint (no prefix) 172 + path('i18n/setlang/', set_language, name='set_language'), 173 + ] 174 + 175 + # All user-facing URLs get language prefix 176 + urlpatterns += i18n_patterns( 177 + path('admin/', admin.site.urls), 178 + path('', include('letters.urls')), 179 + prefix_default_language=True, 180 + ) 181 + 182 + if settings.DEBUG: 183 + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 184 + ``` 185 + 186 + **Step 4: Run test to verify it passes** 187 + 188 + Run: `uv run python manage.py test letters.tests.test_i18n::I18nURLTests -v` 189 + Expected: PASS (3 tests) 190 + 191 + **Step 5: Commit** 192 + 193 + ```bash 194 + git add website/writethem/urls.py 195 + git commit -m "feat: add i18n URL patterns with language prefixes" 196 + ``` 197 + 198 + --- 199 + 200 + ## Task 3: Create Locale Directory Structure 201 + 202 + **Files:** 203 + - Create: `website/locale/` directory structure 204 + 205 + **Step 1: Create directory structure** 206 + 207 + Run: 208 + ```bash 209 + cd website 210 + mkdir -p locale/de/LC_MESSAGES 211 + mkdir -p locale/en/LC_MESSAGES 212 + ``` 213 + 214 + **Step 2: Verify directories exist** 215 + 216 + Run: `ls -la locale/` 217 + Expected: Shows `de/` and `en/` directories 218 + 219 + **Step 3: Create .gitkeep files** 220 + 221 + Run: 222 + ```bash 223 + touch locale/de/LC_MESSAGES/.gitkeep 224 + touch locale/en/LC_MESSAGES/.gitkeep 225 + ``` 226 + 227 + This ensures git tracks the directory structure even before .po files are created. 228 + 229 + **Step 4: Commit** 230 + 231 + ```bash 232 + git add locale/ 233 + git commit -m "feat: create locale directory structure for translations" 234 + ``` 235 + 236 + --- 237 + 238 + ## Task 4: Wrap Base Template Strings 239 + 240 + **Files:** 241 + - Modify: `website/letters/templates/letters/base.html` 242 + 243 + **Step 1: Review current base template** 244 + 245 + Run: `cat website/letters/templates/letters/base.html` 246 + 247 + Identify all hardcoded strings that need translation. 248 + 249 + **Step 2: Add i18n load tag and wrap strings** 250 + 251 + At the top of `website/letters/templates/letters/base.html`, add after the first line: 252 + 253 + ```django 254 + {% load i18n %} 255 + ``` 256 + 257 + Then wrap all user-facing strings with `{% trans %}` tags. For example: 258 + 259 + - Navigation links: `{% trans "Home" %}`, `{% trans "Letters" %}`, `{% trans "Login" %}`, etc. 260 + - Button text: `{% trans "Sign Out" %}`, `{% trans "Sign In" %}`, etc. 261 + - Any other UI text 262 + 263 + **Important:** The exact changes depend on the current template content. Wrap every hardcoded user-facing string. 264 + 265 + **Step 3: Test template renders without errors** 266 + 267 + Run: `uv run python manage.py runserver` 268 + Visit: `http://localhost:8000/de/` 269 + Expected: Page loads without template errors (strings still in English because .po files don't exist yet) 270 + 271 + **Step 4: Commit** 272 + 273 + ```bash 274 + git add website/letters/templates/letters/base.html 275 + git commit -m "feat: wrap base template strings with i18n tags" 276 + ``` 277 + 278 + --- 279 + 280 + ## Task 5: Add Language Switcher Component 281 + 282 + **Files:** 283 + - Modify: `website/letters/templates/letters/base.html` 284 + 285 + **Step 1: Write the failing test** 286 + 287 + Add to `website/letters/tests/test_i18n.py`: 288 + 289 + ```python 290 + class LanguageSwitcherTests(TestCase): 291 + def test_language_switcher_present_in_page(self): 292 + """Test that language switcher form is present.""" 293 + response = self.client.get('/de/') 294 + self.assertContains(response, 'name="language"') 295 + self.assertContains(response, 'Deutsch') 296 + self.assertContains(response, 'English') 297 + 298 + def test_language_switch_changes_language(self): 299 + """Test that submitting language form changes language.""" 300 + response = self.client.post( 301 + '/i18n/setlang/', 302 + {'language': 'en', 'next': '/en/'}, 303 + follow=True 304 + ) 305 + self.assertEqual(response.status_code, 200) 306 + # Check cookie was set 307 + self.assertIn('django_language', response.cookies) 308 + ``` 309 + 310 + **Step 2: Run test to verify it fails** 311 + 312 + Run: `uv run python manage.py test letters.tests.test_i18n::LanguageSwitcherTests -v` 313 + Expected: FAIL (language switcher not present) 314 + 315 + **Step 3: Add language switcher to base template** 316 + 317 + In `website/letters/templates/letters/base.html`, add this component in an appropriate location (e.g., in the header/navigation area): 318 + 319 + ```django 320 + <div class="language-switcher"> 321 + <form action="{% url 'set_language' %}" method="post"> 322 + {% csrf_token %} 323 + <input name="next" type="hidden" value="{{ request.get_full_path }}"> 324 + <select name="language" onchange="this.form.submit()" aria-label="{% trans 'Select language' %}"> 325 + {% get_current_language as CURRENT_LANGUAGE %} 326 + {% get_available_languages as AVAILABLE_LANGUAGES %} 327 + {% for lang_code, lang_name in AVAILABLE_LANGUAGES %} 328 + <option value="{{ lang_code }}" {% if lang_code == CURRENT_LANGUAGE %}selected{% endif %}> 329 + {{ lang_name }} 330 + </option> 331 + {% endfor %} 332 + </select> 333 + </form> 334 + </div> 335 + ``` 336 + 337 + **Step 4: Run test to verify it passes** 338 + 339 + Run: `uv run python manage.py test letters.tests.test_i18n::LanguageSwitcherTests -v` 340 + Expected: PASS (2 tests) 341 + 342 + **Step 5: Commit** 343 + 344 + ```bash 345 + git add website/letters/templates/letters/base.html 346 + git commit -m "feat: add language switcher component to base template" 347 + ``` 348 + 349 + --- 350 + 351 + ## Task 6: Wrap Authentication Template Strings 352 + 353 + **Files:** 354 + - Modify: `website/letters/templates/registration/login.html` 355 + - Modify: `website/letters/templates/registration/register.html` 356 + - Modify: `website/letters/templates/registration/password_reset_form.html` 357 + - Modify: `website/letters/templates/registration/password_reset_done.html` 358 + - Modify: `website/letters/templates/registration/password_reset_confirm.html` 359 + - Modify: `website/letters/templates/registration/password_reset_complete.html` 360 + 361 + **Step 1: Add i18n load tag to each template** 362 + 363 + For each template file listed above, add at the top (after `{% extends %}`): 364 + 365 + ```django 366 + {% load i18n %} 367 + ``` 368 + 369 + **Step 2: Wrap all strings with trans tags** 370 + 371 + For each template, wrap user-facing strings: 372 + - Headings: `<h1>{% trans "Login" %}</h1>` 373 + - Labels: `{% trans "Email" %}`, `{% trans "Password" %}` 374 + - Buttons: `{% trans "Sign In" %}`, `{% trans "Register" %}`, `{% trans "Reset Password" %}` 375 + - Messages: `{% trans "Forgot your password?" %}`, etc. 376 + 377 + **Step 3: Test templates render** 378 + 379 + Run: `uv run python manage.py runserver` 380 + Visit each auth page: 381 + - `/de/login/` 382 + - `/de/register/` 383 + - `/de/password-reset/` 384 + 385 + Expected: Pages load without errors 386 + 387 + **Step 4: Commit** 388 + 389 + ```bash 390 + git add website/letters/templates/registration/ 391 + git commit -m "feat: wrap authentication template strings with i18n tags" 392 + ``` 393 + 394 + --- 395 + 396 + ## Task 7: Wrap Letter List and Detail Template Strings 397 + 398 + **Files:** 399 + - Modify: `website/letters/templates/letters/letter_list.html` 400 + - Modify: `website/letters/templates/letters/letter_detail.html` 401 + 402 + **Step 1: Add i18n load tag** 403 + 404 + Add to both templates after `{% extends %}`: 405 + 406 + ```django 407 + {% load i18n %} 408 + ``` 409 + 410 + **Step 2: Wrap strings in letter_list.html** 411 + 412 + Wrap all user-facing strings: 413 + - Headings: `{% trans "Open Letters" %}` 414 + - Buttons: `{% trans "Write Letter" %}`, `{% trans "Filter" %}`, `{% trans "Sort" %}` 415 + - Labels: `{% trans "Topic" %}`, `{% trans "Signatures" %}` 416 + - Empty states: `{% trans "No letters found" %}` 417 + 418 + For pluralization (e.g., signature counts), use `{% blocktrans %}`: 419 + 420 + ```django 421 + {% blocktrans count counter=letter.signatures.count %} 422 + {{ counter }} signature 423 + {% plural %} 424 + {{ counter }} signatures 425 + {% endblocktrans %} 426 + ``` 427 + 428 + **Step 3: Wrap strings in letter_detail.html** 429 + 430 + Wrap all strings: 431 + - Buttons: `{% trans "Sign Letter" %}`, `{% trans "Remove Signature" %}`, `{% trans "Share" %}`, `{% trans "Report" %}` 432 + - Labels: `{% trans "Recipient" %}`, `{% trans "Published" %}`, `{% trans "Signatures" %}` 433 + - Messages: `{% trans "You have signed this letter" %}` 434 + 435 + **Step 4: Test templates render** 436 + 437 + Run: `uv run python manage.py runserver` 438 + Visit: `/de/letters/` and any letter detail page 439 + Expected: Pages load without errors 440 + 441 + **Step 5: Commit** 442 + 443 + ```bash 444 + git add website/letters/templates/letters/letter_list.html website/letters/templates/letters/letter_detail.html 445 + git commit -m "feat: wrap letter list and detail template strings with i18n tags" 446 + ``` 447 + 448 + --- 449 + 450 + ## Task 8: Wrap Letter Creation Template Strings 451 + 452 + **Files:** 453 + - Modify: `website/letters/templates/letters/letter_form.html` 454 + 455 + **Step 1: Add i18n load tag** 456 + 457 + ```django 458 + {% load i18n %} 459 + ``` 460 + 461 + **Step 2: Wrap all strings** 462 + 463 + Wrap: 464 + - Headings: `{% trans "Write an Open Letter" %}` 465 + - Form labels: `{% trans "Title" %}`, `{% trans "Content" %}`, `{% trans "Recipient" %}` 466 + - Help text: `{% trans "Minimum 500 characters" %}` 467 + - Warnings: `{% trans "Once published, letters cannot be edited" %}` 468 + - Buttons: `{% trans "Publish Letter" %}`, `{% trans "Preview" %}`, `{% trans "Cancel" %}` 469 + 470 + **Step 3: Update form class with verbose_name** 471 + 472 + Modify: `website/letters/forms.py` 473 + 474 + Add at the top: 475 + ```python 476 + from django.utils.translation import gettext_lazy as _ 477 + ``` 478 + 479 + For each form field, add `label` parameter: 480 + ```python 481 + title = forms.CharField( 482 + label=_("Title"), 483 + max_length=200, 484 + help_text=_("A clear, concise title for your letter") 485 + ) 486 + ``` 487 + 488 + **Step 4: Test template renders** 489 + 490 + Visit: `/de/letters/new/` 491 + Expected: Page loads without errors 492 + 493 + **Step 5: Commit** 494 + 495 + ```bash 496 + git add website/letters/templates/letters/letter_form.html website/letters/forms.py 497 + git commit -m "feat: wrap letter creation template and form strings with i18n" 498 + ``` 499 + 500 + --- 501 + 502 + ## Task 9: Wrap Profile and Account Template Strings 503 + 504 + **Files:** 505 + - Modify: `website/letters/templates/letters/profile.html` 506 + - Modify: `website/letters/templates/letters/account_delete.html` 507 + - Modify: Any other account-related templates 508 + 509 + **Step 1: Add i18n load tag to each template** 510 + 511 + ```django 512 + {% load i18n %} 513 + ``` 514 + 515 + **Step 2: Wrap all strings** 516 + 517 + Profile page: 518 + - Headings: `{% trans "Your Profile" %}`, `{% trans "Authored Letters" %}`, `{% trans "Signed Letters" %}` 519 + - Buttons: `{% trans "Edit Profile" %}`, `{% trans "Delete Account" %}` 520 + - Labels: `{% trans "Email" %}`, `{% trans "Verified" %}`, `{% trans "Unverified" %}` 521 + 522 + Account deletion: 523 + - Warnings: `{% trans "This action cannot be undone" %}` 524 + - Buttons: `{% trans "Confirm Deletion" %}`, `{% trans "Cancel" %}` 525 + 526 + **Step 3: Test templates render** 527 + 528 + Visit profile and account pages 529 + Expected: Pages load without errors 530 + 531 + **Step 4: Commit** 532 + 533 + ```bash 534 + git add website/letters/templates/letters/profile.html website/letters/templates/letters/account_delete.html 535 + git commit -m "feat: wrap profile and account template strings with i18n tags" 536 + ``` 537 + 538 + --- 539 + 540 + ## Task 10: Extract Translation Strings to .po Files 541 + 542 + **Files:** 543 + - Create: `website/locale/de/LC_MESSAGES/django.po` 544 + - Create: `website/locale/en/LC_MESSAGES/django.po` 545 + 546 + **Step 1: Run makemessages for German** 547 + 548 + Run: 549 + ```bash 550 + cd website 551 + uv run python manage.py makemessages -l de 552 + ``` 553 + 554 + Expected: Creates/updates `locale/de/LC_MESSAGES/django.po` with all translatable strings 555 + 556 + **Step 2: Run makemessages for English** 557 + 558 + Run: 559 + ```bash 560 + uv run python manage.py makemessages -l en 561 + ``` 562 + 563 + Expected: Creates/updates `locale/en/LC_MESSAGES/django.po` 564 + 565 + **Step 3: Verify .po files created** 566 + 567 + Run: 568 + ```bash 569 + ls -la locale/de/LC_MESSAGES/ 570 + ls -la locale/en/LC_MESSAGES/ 571 + ``` 572 + 573 + Expected: Both show `django.po` files 574 + 575 + **Step 4: Check .po file contents** 576 + 577 + Run: 578 + ```bash 579 + head -n 30 locale/de/LC_MESSAGES/django.po 580 + ``` 581 + 582 + Expected: Shows header and first few msgid/msgstr pairs 583 + 584 + **Step 5: Commit** 585 + 586 + ```bash 587 + git add locale/ 588 + git commit -m "feat: extract translatable strings to .po files" 589 + ``` 590 + 591 + --- 592 + 593 + ## Task 11: Translate German Strings in .po File 594 + 595 + **Files:** 596 + - Modify: `website/locale/de/LC_MESSAGES/django.po` 597 + 598 + **Step 1: Open German .po file** 599 + 600 + Open `website/locale/de/LC_MESSAGES/django.po` for editing 601 + 602 + **Step 2: Translate strings systematically** 603 + 604 + Go through each `msgid` and add German translation to `msgstr`: 605 + 606 + ```po 607 + #: letters/templates/letters/base.html:10 608 + msgid "Home" 609 + msgstr "Startseite" 610 + 611 + #: letters/templates/letters/base.html:11 612 + msgid "Letters" 613 + msgstr "Briefe" 614 + 615 + #: letters/templates/letters/base.html:12 616 + msgid "Sign In" 617 + msgstr "Anmelden" 618 + 619 + #: letters/templates/letters/base.html:13 620 + msgid "Sign Out" 621 + msgstr "Abmelden" 622 + 623 + #: letters/templates/letters/letter_list.html:5 624 + msgid "Open Letters" 625 + msgstr "Offene Briefe" 626 + 627 + #: letters/templates/letters/letter_list.html:8 628 + msgid "Write Letter" 629 + msgstr "Brief Schreiben" 630 + 631 + #: letters/templates/letters/letter_detail.html:15 632 + msgid "Sign Letter" 633 + msgstr "Brief Unterschreiben" 634 + 635 + #: letters/templates/letters/letter_detail.html:18 636 + msgid "Remove Signature" 637 + msgstr "Unterschrift Entfernen" 638 + 639 + #: letters/templates/letters/letter_detail.html:21 640 + msgid "Share" 641 + msgstr "Teilen" 642 + 643 + #: letters/templates/letters/letter_form.html:5 644 + msgid "Write an Open Letter" 645 + msgstr "Einen Offenen Brief Schreiben" 646 + 647 + #: letters/templates/letters/letter_form.html:10 648 + msgid "Title" 649 + msgstr "Titel" 650 + 651 + #: letters/templates/letters/letter_form.html:11 652 + msgid "Content" 653 + msgstr "Inhalt" 654 + 655 + #: letters/templates/letters/letter_form.html:12 656 + msgid "Minimum 500 characters" 657 + msgstr "Mindestens 500 Zeichen" 658 + 659 + #: letters/templates/letters/letter_form.html:15 660 + msgid "Once published, letters cannot be edited" 661 + msgstr "Nach Verรถffentlichung kรถnnen Briefe nicht mehr bearbeitet werden" 662 + 663 + #: letters/templates/letters/letter_form.html:20 664 + msgid "Publish Letter" 665 + msgstr "Brief Verรถffentlichen" 666 + 667 + #: letters/templates/registration/login.html:5 668 + msgid "Login" 669 + msgstr "Anmeldung" 670 + 671 + #: letters/templates/registration/login.html:10 672 + msgid "Email" 673 + msgstr "E-Mail" 674 + 675 + #: letters/templates/registration/login.html:11 676 + msgid "Password" 677 + msgstr "Passwort" 678 + 679 + #: letters/templates/registration/login.html:15 680 + msgid "Forgot your password?" 681 + msgstr "Passwort vergessen?" 682 + 683 + #: letters/templates/registration/register.html:5 684 + msgid "Register" 685 + msgstr "Registrieren" 686 + ``` 687 + 688 + **Note:** The exact strings will depend on what was extracted in Task 10. Translate ALL msgid entries systematically. 689 + 690 + **Step 3: Save the file** 691 + 692 + Ensure all translations are complete (no empty `msgstr ""` entries) 693 + 694 + **Step 4: Commit** 695 + 696 + ```bash 697 + git add locale/de/LC_MESSAGES/django.po 698 + git commit -m "feat: add German translations to .po file" 699 + ``` 700 + 701 + --- 702 + 703 + ## Task 12: Populate English .po File 704 + 705 + **Files:** 706 + - Modify: `website/locale/en/LC_MESSAGES/django.po` 707 + 708 + **Step 1: Open English .po file** 709 + 710 + Open `website/locale/en/LC_MESSAGES/django.po` for editing 711 + 712 + **Step 2: Add identity translations** 713 + 714 + For English, most translations are identity (msgstr = msgid): 715 + 716 + ```po 717 + #: letters/templates/letters/base.html:10 718 + msgid "Home" 719 + msgstr "Home" 720 + 721 + #: letters/templates/letters/base.html:11 722 + msgid "Letters" 723 + msgstr "Letters" 724 + ``` 725 + 726 + Go through all entries and copy msgid to msgstr (they should be identical for English). 727 + 728 + **Step 3: Save the file** 729 + 730 + **Step 4: Commit** 731 + 732 + ```bash 733 + git add locale/en/LC_MESSAGES/django.po 734 + git commit -m "feat: add English identity translations to .po file" 735 + ``` 736 + 737 + --- 738 + 739 + ## Task 13: Compile Translation Files 740 + 741 + **Files:** 742 + - Create: `website/locale/de/LC_MESSAGES/django.mo` 743 + - Create: `website/locale/en/LC_MESSAGES/django.mo` 744 + 745 + **Step 1: Run compilemessages** 746 + 747 + Run: 748 + ```bash 749 + cd website 750 + uv run python manage.py compilemessages 751 + ``` 752 + 753 + Expected: Creates `django.mo` files for both German and English 754 + 755 + **Step 2: Verify .mo files created** 756 + 757 + Run: 758 + ```bash 759 + ls -la locale/de/LC_MESSAGES/ 760 + ls -la locale/en/LC_MESSAGES/ 761 + ``` 762 + 763 + Expected: Both show `django.mo` files (binary format) 764 + 765 + **Step 3: Test translations work** 766 + 767 + Run: `uv run python manage.py runserver` 768 + 769 + Visit `/de/` - should show German interface 770 + Visit `/en/` - should show English interface 771 + 772 + Use language switcher to toggle between languages. 773 + 774 + **Step 4: Add .mo files to .gitignore** 775 + 776 + Modify `.gitignore` in repository root, add: 777 + ``` 778 + # Compiled translation files (generated from .po) 779 + *.mo 780 + ``` 781 + 782 + **Note:** .mo files are generated artifacts and don't need to be tracked in git. 783 + 784 + **Step 5: Commit** 785 + 786 + ```bash 787 + git add .gitignore 788 + git commit -m "chore: add compiled translation files to .gitignore" 789 + ``` 790 + 791 + --- 792 + 793 + ## Task 14: Create Translation Completeness Check Command 794 + 795 + **Files:** 796 + - Create: `website/letters/management/commands/check_translations.py` 797 + 798 + **Step 1: Write the failing test** 799 + 800 + Add to `website/letters/tests/test_i18n.py`: 801 + 802 + ```python 803 + from django.core.management import call_command 804 + from io import StringIO 805 + 806 + 807 + class TranslationCompletenessTests(TestCase): 808 + def test_check_translations_command_exists(self): 809 + """Test that check_translations command can be called.""" 810 + out = StringIO() 811 + call_command('check_translations', stdout=out) 812 + output = out.getvalue() 813 + self.assertIn('Deutsch', output) 814 + self.assertIn('English', output) 815 + ``` 816 + 817 + **Step 2: Run test to verify it fails** 818 + 819 + Run: `uv run python manage.py test letters.tests.test_i18n::TranslationCompletenessTests -v` 820 + Expected: FAIL (command doesn't exist) 821 + 822 + **Step 3: Create management command** 823 + 824 + Create: `website/letters/management/commands/check_translations.py` 825 + 826 + ```python 827 + # ABOUTME: Management command to check translation completeness and report coverage. 828 + # ABOUTME: Analyzes .po files to find untranslated strings and calculate coverage percentage. 829 + 830 + from django.core.management.base import BaseCommand 831 + from django.conf import settings 832 + import pathlib 833 + 834 + 835 + class Command(BaseCommand): 836 + help = "Check translation completeness for all configured languages" 837 + 838 + def add_arguments(self, parser): 839 + parser.add_argument( 840 + '--language', 841 + type=str, 842 + help='Check specific language (e.g., "de" or "en")', 843 + ) 844 + 845 + def handle(self, *args, **options): 846 + locale_paths = settings.LOCALE_PATHS 847 + languages = settings.LANGUAGES 848 + 849 + target_language = options.get('language') 850 + 851 + if target_language: 852 + languages_to_check = [(target_language, None)] 853 + else: 854 + languages_to_check = languages 855 + 856 + for lang_code, lang_name in languages_to_check: 857 + self.check_language(locale_paths[0], lang_code, lang_name) 858 + 859 + def check_language(self, locale_path, lang_code, lang_name): 860 + """Check translation completeness for a single language.""" 861 + po_file = pathlib.Path(locale_path) / lang_code / 'LC_MESSAGES' / 'django.po' 862 + 863 + if not po_file.exists(): 864 + self.stdout.write(self.style.ERROR( 865 + f"\n{lang_code}: No .po file found at {po_file}" 866 + )) 867 + return 868 + 869 + total = 0 870 + translated = 0 871 + untranslated = [] 872 + 873 + with open(po_file, 'r', encoding='utf-8') as f: 874 + current_msgid = None 875 + for line in f: 876 + line = line.strip() 877 + if line.startswith('msgid "') and not line.startswith('msgid ""'): 878 + current_msgid = line[7:-1] # Extract string between quotes 879 + total += 1 880 + elif line.startswith('msgstr "'): 881 + msgstr = line[8:-1] 882 + if msgstr: # Non-empty translation 883 + translated += 1 884 + elif current_msgid: 885 + untranslated.append(current_msgid) 886 + current_msgid = None 887 + 888 + if total == 0: 889 + self.stdout.write(self.style.WARNING( 890 + f"\n{lang_code}: No translatable strings found" 891 + )) 892 + return 893 + 894 + coverage = (translated / total) * 100 895 + display_name = lang_name if lang_name else lang_code 896 + 897 + self.stdout.write(self.style.SUCCESS( 898 + f"\n{display_name} ({lang_code}):" 899 + )) 900 + self.stdout.write(f" Total strings: {total}") 901 + self.stdout.write(f" Translated: {translated}") 902 + self.stdout.write(f" Untranslated: {len(untranslated)}") 903 + self.stdout.write(f" Coverage: {coverage:.1f}%") 904 + 905 + if untranslated: 906 + self.stdout.write(self.style.WARNING( 907 + f"\nMissing translations ({len(untranslated)}):" 908 + )) 909 + for msgid in untranslated[:10]: # Show first 10 910 + self.stdout.write(f" - {msgid}") 911 + if len(untranslated) > 10: 912 + self.stdout.write(f" ... and {len(untranslated) - 10} more") 913 + else: 914 + self.stdout.write(self.style.SUCCESS( 915 + "\nAll strings translated!" 916 + )) 917 + ``` 918 + 919 + **Step 4: Run test to verify it passes** 920 + 921 + Run: `uv run python manage.py test letters.tests.test_i18n::TranslationCompletenessTests -v` 922 + Expected: PASS 923 + 924 + **Step 5: Test command manually** 925 + 926 + Run: 927 + ```bash 928 + uv run python manage.py check_translations 929 + ``` 930 + 931 + Expected: Shows coverage report for both German and English 932 + 933 + **Step 6: Commit** 934 + 935 + ```bash 936 + git add website/letters/management/commands/check_translations.py 937 + git commit -m "feat: add check_translations management command" 938 + ``` 939 + 940 + --- 941 + 942 + ## Task 15: Update Documentation 943 + 944 + **Files:** 945 + - Modify: `README.md` 946 + - Modify: `docs/matching-algorithm.md` (add i18n section) 947 + 948 + **Step 1: Update README with i18n information** 949 + 950 + Add a new section to `README.md`: 951 + 952 + ```markdown 953 + ## Internationalization 954 + 955 + WriteThem.eu supports German (default) and English. 956 + 957 + ### Using the Site 958 + 959 + - Visit `/de/` for German interface 960 + - Visit `/en/` for English interface 961 + - Use the language switcher in the header to toggle languages 962 + - Language preference is saved in a cookie 963 + 964 + ### For Developers 965 + 966 + **Translation workflow:** 967 + 968 + 1. Wrap new UI strings with translation functions: 969 + - Templates: `{% trans "Text" %}` or `{% blocktrans %}` 970 + - Python: `gettext()` or `gettext_lazy()` 971 + 972 + 2. Extract strings to .po files: 973 + ```bash 974 + cd website 975 + uv run python manage.py makemessages -l de -l en 976 + ``` 977 + 978 + 3. Translate strings in `.po` files: 979 + - Edit `locale/de/LC_MESSAGES/django.po` (German translations) 980 + - Edit `locale/en/LC_MESSAGES/django.po` (English, mostly identity translations) 981 + 982 + 4. Compile translations: 983 + ```bash 984 + uv run python manage.py compilemessages 985 + ``` 986 + 987 + 5. Check translation completeness: 988 + ```bash 989 + uv run python manage.py check_translations 990 + ``` 991 + 992 + **Important:** All code, comments, and translation keys should be in English. Only .po files contain actual translations. 993 + ``` 994 + 995 + **Step 2: Add i18n section to matching-algorithm.md** 996 + 997 + Add at the end of `docs/matching-algorithm.md`: 998 + 999 + ```markdown 1000 + ## Internationalization 1001 + 1002 + The constituency matching system works identically in both German and English: 1003 + 1004 + - Addresses can be entered in German format (standard use case) 1005 + - UI language (German/English) does not affect geocoding or matching logic 1006 + - Representative names, constituency names, and geographic data remain in original German 1007 + - All user-facing labels and messages are translated 1008 + ``` 1009 + 1010 + **Step 3: Commit** 1011 + 1012 + ```bash 1013 + git add README.md docs/matching-algorithm.md 1014 + git commit -m "docs: add internationalization documentation" 1015 + ``` 1016 + 1017 + --- 1018 + 1019 + ## Task 16: Run Full Test Suite and Verify 1020 + 1021 + **Step 1: Run all tests** 1022 + 1023 + Run: 1024 + ```bash 1025 + cd website 1026 + uv run python manage.py test letters.tests.test_i18n letters.tests.test_address_matching letters.tests.test_topic_mapping letters.tests.test_constituency_suggestions 1027 + ``` 1028 + 1029 + Expected: All tests pass (check total count) 1030 + 1031 + **Step 2: Check translation completeness** 1032 + 1033 + Run: 1034 + ```bash 1035 + uv run python manage.py check_translations 1036 + ``` 1037 + 1038 + Expected: 100% coverage for both languages (or report any missing translations) 1039 + 1040 + **Step 3: Manual verification checklist** 1041 + 1042 + Run: `uv run python manage.py runserver` 1043 + 1044 + Test each page in both languages: 1045 + 1046 + **German (`/de/`):** 1047 + - [ ] Homepage loads in German 1048 + - [ ] Login page in German 1049 + - [ ] Register page in German 1050 + - [ ] Letter list in German 1051 + - [ ] Letter detail in German 1052 + - [ ] Letter creation form in German 1053 + - [ ] Profile page in German 1054 + - [ ] Language switcher works (toggles to English) 1055 + 1056 + **English (`/en/`):** 1057 + - [ ] Homepage loads in English 1058 + - [ ] Login page in English 1059 + - [ ] Register page in English 1060 + - [ ] Letter list in English 1061 + - [ ] Letter detail in English 1062 + - [ ] Letter creation form in English 1063 + - [ ] Profile page in English 1064 + - [ ] Language switcher works (toggles to German) 1065 + 1066 + **Step 4: Check for untranslated strings** 1067 + 1068 + While testing, look for any English text appearing on German pages (or vice versa). These indicate missed translations. 1069 + 1070 + If found, add them to .po files, compile, and test again. 1071 + 1072 + **Step 5: Create summary commit** 1073 + 1074 + ```bash 1075 + git add . 1076 + git commit -m "test: verify i18n implementation with full test suite" 1077 + ``` 1078 + 1079 + --- 1080 + 1081 + ## Verification Checklist 1082 + 1083 + Before merging this feature: 1084 + 1085 + - [ ] USE_I18N=True in settings 1086 + - [ ] LANGUAGES configured with German and English 1087 + - [ ] LOCALE_PATHS configured 1088 + - [ ] LocaleMiddleware added to MIDDLEWARE 1089 + - [ ] URL patterns use i18n_patterns() 1090 + - [ ] Language switcher present in base template 1091 + - [ ] All templates have `{% load i18n %}` 1092 + - [ ] All UI strings wrapped with `{% trans %}` or `{% blocktrans %}` 1093 + - [ ] German .po file fully translated (100% coverage) 1094 + - [ ] English .po file complete (identity translations) 1095 + - [ ] .mo files compile without errors 1096 + - [ ] check_translations command works 1097 + - [ ] All automated tests pass 1098 + - [ ] Manual testing in both languages successful 1099 + - [ ] Documentation updated 1100 + - [ ] No untranslated strings visible in UI 1101 + 1102 + --- 1103 + 1104 + ## Notes for Implementation 1105 + 1106 + **Language policy:** 1107 + - All code (variables, functions, classes): English 1108 + - All comments and docstrings: English 1109 + - All translation keys (msgid in .po): English 1110 + - .po files contain actual translations 1111 + 1112 + **Testing strategy:** 1113 + - TDD throughout: write test โ†’ verify fail โ†’ implement โ†’ verify pass โ†’ commit 1114 + - Run `uv run python manage.py test` frequently 1115 + - Use `uv run python manage.py runserver` for manual verification 1116 + - Use `check_translations` command to catch missing translations 1117 + 1118 + **Common pitfalls:** 1119 + - Forgetting `{% load i18n %}` at top of templates 1120 + - Not using `gettext_lazy` in models/forms (use lazy version for class-level strings) 1121 + - Mixing `{% trans %}` and `{% blocktrans %}` incorrectly (use blocktrans for variables) 1122 + - Not recompiling after editing .po files (run compilemessages) 1123 + 1124 + **Skills to reference:** 1125 + - @skills/testing/test-driven-development for TDD workflow 1126 + - @skills/debugging/systematic-debugging if tests fail unexpectedly 1127 + - @skills/collaboration/finishing-a-development-branch when merging back to feat/matching
+90
website/letters/management/commands/check_translations.py
···
··· 1 + # ABOUTME: Management command to check translation completeness and report coverage. 2 + # ABOUTME: Analyzes .po files to find untranslated strings and calculate coverage percentage. 3 + 4 + from django.core.management.base import BaseCommand 5 + from django.conf import settings 6 + import pathlib 7 + 8 + 9 + class Command(BaseCommand): 10 + help = "Check translation completeness for all configured languages" 11 + 12 + def add_arguments(self, parser): 13 + parser.add_argument( 14 + '--language', 15 + type=str, 16 + help='Check specific language (e.g., "de" or "en")', 17 + ) 18 + 19 + def handle(self, *args, **options): 20 + locale_paths = settings.LOCALE_PATHS 21 + languages = settings.LANGUAGES 22 + 23 + target_language = options.get('language') 24 + 25 + if target_language: 26 + languages_to_check = [(target_language, None)] 27 + else: 28 + languages_to_check = languages 29 + 30 + for lang_code, lang_name in languages_to_check: 31 + self.check_language(locale_paths[0], lang_code, lang_name) 32 + 33 + def check_language(self, locale_path, lang_code, lang_name): 34 + """Check translation completeness for a single language.""" 35 + po_file = pathlib.Path(locale_path) / lang_code / 'LC_MESSAGES' / 'django.po' 36 + 37 + if not po_file.exists(): 38 + self.stdout.write(self.style.ERROR( 39 + f"\n{lang_code}: No .po file found at {po_file}" 40 + )) 41 + return 42 + 43 + total = 0 44 + translated = 0 45 + untranslated = [] 46 + 47 + with open(po_file, 'r', encoding='utf-8') as f: 48 + current_msgid = None 49 + for line in f: 50 + line = line.strip() 51 + if line.startswith('msgid "') and not line.startswith('msgid ""'): 52 + current_msgid = line[7:-1] # Extract string between quotes 53 + total += 1 54 + elif line.startswith('msgstr "'): 55 + msgstr = line[8:-1] 56 + if msgstr: # Non-empty translation 57 + translated += 1 58 + elif current_msgid: 59 + untranslated.append(current_msgid) 60 + current_msgid = None 61 + 62 + if total == 0: 63 + self.stdout.write(self.style.WARNING( 64 + f"\n{lang_code}: No translatable strings found" 65 + )) 66 + return 67 + 68 + coverage = (translated / total) * 100 69 + display_name = lang_name if lang_name else lang_code 70 + 71 + self.stdout.write(self.style.SUCCESS( 72 + f"\n{display_name} ({lang_code}):" 73 + )) 74 + self.stdout.write(f" Total strings: {total}") 75 + self.stdout.write(f" Translated: {translated}") 76 + self.stdout.write(f" Untranslated: {len(untranslated)}") 77 + self.stdout.write(f" Coverage: {coverage:.1f}%") 78 + 79 + if untranslated: 80 + self.stdout.write(self.style.WARNING( 81 + f"\nMissing translations ({len(untranslated)}):" 82 + )) 83 + for msgid in untranslated[:10]: # Show first 10 84 + self.stdout.write(f" - {msgid}") 85 + if len(untranslated) > 10: 86 + self.stdout.write(f" ... and {len(untranslated) - 10} more") 87 + else: 88 + self.stdout.write(self.style.SUCCESS( 89 + "\nAll strings translated!" 90 + ))
+174
website/letters/management/commands/db_snapshot.py
···
··· 1 + # ABOUTME: Management command to save and restore SQLite database snapshots. 2 + # ABOUTME: Enables quick database state preservation for testing and development. 3 + 4 + import shutil 5 + import pathlib 6 + from datetime import datetime 7 + 8 + from django.core.management.base import BaseCommand, CommandError 9 + from django.conf import settings 10 + from letters.models import Parliament, Constituency, Representative, Committee, TopicArea 11 + 12 + 13 + class Command(BaseCommand): 14 + help = "Save or restore SQLite database snapshots to/from fixtures directory" 15 + 16 + def add_arguments(self, parser): 17 + subparsers = parser.add_subparsers( 18 + dest="subcommand", help="Available subcommands", title="subcommands" 19 + ) 20 + 21 + # Save subcommand 22 + save_parser = subparsers.add_parser( 23 + "save", help="Create a snapshot of the current database state" 24 + ) 25 + save_parser.add_argument( 26 + "description", 27 + type=str, 28 + help='Description of the snapshot (e.g., "with-representatives", "after-sync")', 29 + ) 30 + 31 + # Restore subcommand 32 + restore_parser = subparsers.add_parser( 33 + "restore", help="Restore database from a snapshot" 34 + ) 35 + restore_parser.add_argument( 36 + "snapshot_file", 37 + type=str, 38 + help='Snapshot filename (e.g., "db_snapshot_20241014_123456_with-representatives.sqlite3")', 39 + ) 40 + 41 + # List subcommand 42 + subparsers.add_parser("list", help="List available snapshots") 43 + 44 + def handle(self, *args, **options): 45 + subcommand = options.get("subcommand") 46 + 47 + if not subcommand: 48 + self.print_help("manage.py", "db_snapshot") 49 + return 50 + 51 + if subcommand == "save": 52 + self.handle_save(options) 53 + elif subcommand == "restore": 54 + self.handle_restore(options) 55 + elif subcommand == "list": 56 + self.handle_list(options) 57 + else: 58 + raise CommandError(f"Unknown subcommand: {subcommand}") 59 + 60 + def handle_save(self, options): 61 + """Create a timestamped snapshot of the current database""" 62 + description = options["description"] 63 + 64 + # Create filename with timestamp and description 65 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 66 + # Sanitize description for filename 67 + safe_description = "".join(c if c.isalnum() or c == "-" else "_" for c in description) 68 + snapshot_filename = f"db_snapshot_{timestamp}_{safe_description}.sqlite3" 69 + 70 + # Determine source database path 71 + db_path = pathlib.Path(settings.DATABASES["default"]["NAME"]) 72 + if not db_path.exists(): 73 + raise CommandError(f"Database file not found: {db_path}") 74 + 75 + # Create fixtures directory if it doesn't exist 76 + fixtures_dir = settings.BASE_DIR.parent / "fixtures" 77 + fixtures_dir.mkdir(exist_ok=True) 78 + 79 + snapshot_path = fixtures_dir / snapshot_filename 80 + 81 + self.stdout.write(self.style.SUCCESS("๐Ÿ“ธ Creating database snapshot...")) 82 + self.stdout.write(f" Source: {db_path}") 83 + self.stdout.write(f" Destination: {snapshot_path}") 84 + self.stdout.write(f" Description: {description}") 85 + 86 + # Copy the SQLite database file 87 + shutil.copy2(db_path, snapshot_path) 88 + 89 + self.stdout.write(self.style.SUCCESS("\nโœ… Snapshot created successfully!")) 90 + self.stdout.write(f" File: {snapshot_filename}") 91 + self.stdout.write(f" Size: {snapshot_path.stat().st_size / (1024 * 1024):.2f} MB") 92 + 93 + # Show current database stats 94 + self.stdout.write(self.style.SUCCESS("\n๐Ÿ“Š Snapshot contains:")) 95 + self.stdout.write(f" ๐Ÿ›๏ธ Parliaments: {Parliament.objects.count()}") 96 + self.stdout.write(f" ๐Ÿ“ Constituencies: {Constituency.objects.count()}") 97 + self.stdout.write(f" ๐Ÿ‘ค Representatives: {Representative.objects.count()}") 98 + self.stdout.write(f" ๐Ÿข Committees: {Committee.objects.count()}") 99 + self.stdout.write(f" ๐Ÿท๏ธ Topic Areas: {TopicArea.objects.count()}") 100 + 101 + def handle_restore(self, options): 102 + """Restore database from a snapshot""" 103 + snapshot_file = options["snapshot_file"] 104 + 105 + # Locate snapshot file 106 + fixtures_dir = settings.BASE_DIR.parent / "fixtures" 107 + snapshot_path = fixtures_dir / snapshot_file 108 + 109 + if not snapshot_path.exists(): 110 + raise CommandError(f"Snapshot file not found: {snapshot_path}") 111 + 112 + # Determine target database path 113 + db_path = pathlib.Path(settings.DATABASES["default"]["NAME"]) 114 + 115 + self.stdout.write(self.style.WARNING("โš ๏ธ Restoring database snapshot...")) 116 + self.stdout.write(f" Source: {snapshot_path}") 117 + self.stdout.write(f" Destination: {db_path}") 118 + self.stdout.write(f" Size: {snapshot_path.stat().st_size / (1024 * 1024):.2f} MB") 119 + 120 + # Confirm overwrite 121 + self.stdout.write( 122 + self.style.WARNING( 123 + "\nโš ๏ธ This will OVERWRITE your current database!" 124 + ) 125 + ) 126 + confirm = input("Type 'restore' to confirm: ") 127 + 128 + if confirm != "restore": 129 + self.stdout.write(self.style.ERROR("Restore cancelled.")) 130 + return 131 + 132 + # Backup current database before overwriting 133 + if db_path.exists(): 134 + backup_path = db_path.parent / f"{db_path.stem}_backup_before_restore{db_path.suffix}" 135 + self.stdout.write(f"\n๐Ÿ“ฆ Backing up current database to: {backup_path.name}") 136 + shutil.copy2(db_path, backup_path) 137 + 138 + # Restore the snapshot 139 + shutil.copy2(snapshot_path, db_path) 140 + 141 + self.stdout.write(self.style.SUCCESS("\nโœ… Database restored successfully!")) 142 + 143 + # Show restored database stats 144 + self.stdout.write(self.style.SUCCESS("\n๐Ÿ“Š Restored database contains:")) 145 + self.stdout.write(f" ๐Ÿ›๏ธ Parliaments: {Parliament.objects.count()}") 146 + self.stdout.write(f" ๐Ÿ“ Constituencies: {Constituency.objects.count()}") 147 + self.stdout.write(f" ๐Ÿ‘ค Representatives: {Representative.objects.count()}") 148 + self.stdout.write(f" ๐Ÿข Committees: {Committee.objects.count()}") 149 + self.stdout.write(f" ๐Ÿท๏ธ Topic Areas: {TopicArea.objects.count()}") 150 + 151 + def handle_list(self, options): 152 + """List available snapshots""" 153 + fixtures_dir = settings.BASE_DIR.parent / "fixtures" 154 + 155 + if not fixtures_dir.exists(): 156 + self.stdout.write(self.style.WARNING("No fixtures directory found.")) 157 + return 158 + 159 + # Find all snapshot files 160 + snapshots = sorted(fixtures_dir.glob("db_snapshot_*.sqlite3"), reverse=True) 161 + 162 + if not snapshots: 163 + self.stdout.write(self.style.WARNING("No snapshots found.")) 164 + return 165 + 166 + self.stdout.write(self.style.SUCCESS(f"\n๐Ÿ“ Available snapshots ({len(snapshots)}):")) 167 + self.stdout.write("") 168 + 169 + for snapshot in snapshots: 170 + size_mb = snapshot.stat().st_size / (1024 * 1024) 171 + mtime = datetime.fromtimestamp(snapshot.stat().st_mtime) 172 + self.stdout.write(f" โ€ข {snapshot.name}") 173 + self.stdout.write(f" Size: {size_mb:.2f} MB, Modified: {mtime.strftime('%Y-%m-%d %H:%M:%S')}") 174 + self.stdout.write("")
+27 -1
website/letters/templates/letters/base.html
··· 35 border-radius: 3px; 36 } 37 nav a:hover { background: #34495e; } 38 .messages { 39 list-style: none; 40 margin-bottom: 1rem; ··· 123 <h1>WriteThem.eu</h1> 124 <nav> 125 <a href="{% url 'letter_list' %}">{% trans "Letters" %}</a> 126 - <a href="{% url 'competency_overview' %}">Kompetenzen</a> 127 {% if user.is_authenticated %} 128 <a href="{% url 'letter_create' %}">{% trans "Write Letter" %}</a> 129 <a href="{% url 'profile' %}">{% trans "Profile" %}</a> ··· 135 {% if user.is_staff %} 136 <a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> 137 {% endif %} 138 </nav> 139 </div> 140 </header>
··· 35 border-radius: 3px; 36 } 37 nav a:hover { background: #34495e; } 38 + .language-switcher { 39 + display: inline-block; 40 + margin-left: 1rem; 41 + } 42 + .language-switcher select { 43 + background: #34495e; 44 + color: white; 45 + border: 1px solid #4a6278; 46 + border-radius: 3px; 47 + padding: 0.3rem 0.6rem; 48 + cursor: pointer; 49 + } 50 + .language-switcher select:hover { 51 + background: #415a77; 52 + } 53 .messages { 54 list-style: none; 55 margin-bottom: 1rem; ··· 138 <h1>WriteThem.eu</h1> 139 <nav> 140 <a href="{% url 'letter_list' %}">{% trans "Letters" %}</a> 141 + <a href="{% url 'competency_overview' %}">{% trans "Competencies" %}</a> 142 {% if user.is_authenticated %} 143 <a href="{% url 'letter_create' %}">{% trans "Write Letter" %}</a> 144 <a href="{% url 'profile' %}">{% trans "Profile" %}</a> ··· 150 {% if user.is_staff %} 151 <a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> 152 {% endif %} 153 + <div class="language-switcher"> 154 + <form action="{% url 'set_language' %}" method="post"> 155 + {% csrf_token %} 156 + <input name="next" type="hidden" value="{{ request.get_full_path }}"> 157 + <select name="language" onchange="this.form.submit()" aria-label="{% trans 'Select language' %}"> 158 + {% get_current_language as CURRENT_LANGUAGE %} 159 + <option value="de" {% if CURRENT_LANGUAGE == 'de' %}selected{% endif %}>Deutsch</option> 160 + <option value="en" {% if CURRENT_LANGUAGE == 'en' %}selected{% endif %}>English</option> 161 + </select> 162 + </form> 163 + </div> 164 </nav> 165 </div> 166 </header>
+2 -2
website/letters/templates/letters/letter_detail.html
··· 55 <div style="padding: 0.8rem 0; border-bottom: 1px solid #ecf0f1;"> 56 <strong>{{ signature.display_name }}</strong> 57 {% if signature.is_verified_constituent %} 58 - <span class="badge badge-verified">โœ“ Verified Constituent</span> 59 {% elif signature.is_verified_non_constituent %} 60 - <span class="badge badge-verified">โœ“ Verified</span> 61 {% endif %} 62 <p style="color: #7f8c8d; font-size: 0.85rem;">{{ signature.signed_at|date:"M d, Y" }}</p> 63 {% if signature.comment %}
··· 55 <div style="padding: 0.8rem 0; border-bottom: 1px solid #ecf0f1;"> 56 <strong>{{ signature.display_name }}</strong> 57 {% if signature.is_verified_constituent %} 58 + <span class="badge badge-verified">{% trans "โœ“ Verified Constituent" %}</span> 59 {% elif signature.is_verified_non_constituent %} 60 + <span class="badge badge-verified">{% trans "โœ“ Verified" %}</span> 61 {% endif %} 62 <p style="color: #7f8c8d; font-size: 0.85rem;">{{ signature.signed_at|date:"M d, Y" }}</p> 63 {% if signature.comment %}
+4 -4
website/letters/templates/letters/letter_list.html
··· 38 {% if is_paginated %} 39 <div style="text-align: center; margin: 2rem 0;"> 40 {% if page_obj.has_previous %} 41 - <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-secondary">Previous</a> 42 {% endif %} 43 - <span style="margin: 0 1rem;">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span> 44 {% if page_obj.has_next %} 45 - <a href="?page={{ page_obj.next_page_number }}" class="btn btn-secondary">Next</a> 46 {% endif %} 47 </div> 48 {% endif %} 49 {% else %} 50 <div class="card"> 51 - <p>No letters found. <a href="{% url 'letter_create' %}">Be the first to write one!</a></p> 52 </div> 53 {% endif %} 54 {% endblock %}
··· 38 {% if is_paginated %} 39 <div style="text-align: center; margin: 2rem 0;"> 40 {% if page_obj.has_previous %} 41 + <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-secondary">{% trans "Previous" %}</a> 42 {% endif %} 43 + <span style="margin: 0 1rem;">{% blocktrans with page=page_obj.number total=page_obj.paginator.num_pages %}Page {{ page }} of {{ total }}{% endblocktrans %}</span> 44 {% if page_obj.has_next %} 45 + <a href="?page={{ page_obj.next_page_number }}" class="btn btn-secondary">{% trans "Next" %}</a> 46 {% endif %} 47 </div> 48 {% endif %} 49 {% else %} 50 <div class="card"> 51 + <p>{% trans "No letters found." %} <a href="{% url 'letter_create' %}">{% trans "Be the first to write one!" %}</a></p> 52 </div> 53 {% endif %} 54 {% endblock %}
+89
website/letters/tests/test_i18n.py
···
··· 1 + # ABOUTME: Tests for internationalization configuration and functionality. 2 + # ABOUTME: Verifies language switching, URL prefixes, and translation completeness. 3 + 4 + from django.test import TestCase 5 + from django.conf import settings 6 + from django.core.management import call_command 7 + from io import StringIO 8 + 9 + 10 + class I18nConfigurationTests(TestCase): 11 + def test_i18n_enabled(self): 12 + """Test that USE_I18N is enabled.""" 13 + self.assertTrue(settings.USE_I18N) 14 + 15 + def test_supported_languages(self): 16 + """Test that German and English are configured.""" 17 + language_codes = [code for code, name in settings.LANGUAGES] 18 + self.assertIn('de', language_codes) 19 + self.assertIn('en', language_codes) 20 + 21 + def test_locale_paths_configured(self): 22 + """Test that LOCALE_PATHS is set.""" 23 + self.assertTrue(len(settings.LOCALE_PATHS) > 0) 24 + 25 + 26 + class I18nURLTests(TestCase): 27 + def test_german_url_prefix_works(self): 28 + """Test that German URL prefix is accessible.""" 29 + response = self.client.get('/de/') 30 + self.assertEqual(response.status_code, 200) 31 + 32 + def test_english_url_prefix_works(self): 33 + """Test that English URL prefix is accessible.""" 34 + response = self.client.get('/en/') 35 + self.assertEqual(response.status_code, 200) 36 + 37 + def test_set_language_endpoint_exists(self): 38 + """Test that language switcher endpoint exists.""" 39 + from django.urls import reverse 40 + url = reverse('set_language') 41 + self.assertEqual(url, '/i18n/setlang/') 42 + 43 + 44 + class LanguageSwitcherTests(TestCase): 45 + def test_language_switcher_present_in_page(self): 46 + """Test that language switcher form is present.""" 47 + response = self.client.get('/de/') 48 + self.assertContains(response, 'name="language"') 49 + self.assertContains(response, 'Deutsch') 50 + self.assertContains(response, 'English') 51 + 52 + def test_language_switch_changes_language(self): 53 + """Test that submitting language form changes language.""" 54 + response = self.client.post( 55 + '/i18n/setlang/', 56 + {'language': 'en', 'next': '/en/'}, 57 + ) 58 + # Check we got a redirect 59 + self.assertEqual(response.status_code, 302) 60 + # Check cookie was set 61 + self.assertIn('django_language', response.cookies) 62 + self.assertEqual(response.cookies['django_language'].value, 'en') 63 + 64 + 65 + class LetterFormI18nTests(TestCase): 66 + def test_letter_form_template_renders(self): 67 + """Test that letter creation form renders without errors.""" 68 + from django.contrib.auth.models import User 69 + # Create a user and log them in 70 + user = User.objects.create_user(username='testuser', password='testpass') 71 + self.client.login(username='testuser', password='testpass') 72 + 73 + # Test German version 74 + response = self.client.get('/de/letter/new/') 75 + self.assertEqual(response.status_code, 200) 76 + 77 + # Test English version 78 + response = self.client.get('/en/letter/new/') 79 + self.assertEqual(response.status_code, 200) 80 + 81 + 82 + class TranslationCompletenessTests(TestCase): 83 + def test_check_translations_command_exists(self): 84 + """Test that check_translations command can be called.""" 85 + out = StringIO() 86 + call_command('check_translations', stdout=out) 87 + output = out.getvalue() 88 + self.assertIn('Deutsch', output) 89 + self.assertIn('English', output)
website/locale/de/LC_MESSAGES/.gitkeep

This is a binary file and will not be displayed.

website/locale/de/LC_MESSAGES/django.mo

This is a binary file and will not be displayed.

+840 -409
website/locale/de/LC_MESSAGES/django.po
··· 6 msgstr "" 7 "Project-Id-Version: WriteThem.eu\n" 8 "Report-Msgid-Bugs-To: \n" 9 - "POT-Creation-Date: 2025-10-04 22:57+0200\n" 10 "PO-Revision-Date: 2025-01-10 12:00+0100\n" 11 "Last-Translator: \n" 12 "Language-Team: German\n" ··· 16 "Content-Transfer-Encoding: 8bit\n" 17 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 19 - # Forms 20 - #: letters/forms.py:28 21 - msgid "Postal code (PLZ)" 22 - msgstr "Postleitzahl (PLZ)" 23 24 - #: letters/forms.py:29 25 - #, fuzzy 26 - #| msgid "Use your PLZ to narrow down representatives from your constituency." 27 - msgid "Use your PLZ to narrow down representatives from your parliament." 28 - msgstr "" 29 - "Verwenden Sie Ihre PLZ, um Abgeordnete aus Ihrem Wahlkreis einzugrenzen." 30 31 - #: letters/forms.py:30 32 - msgid "e.g. 10115" 33 - msgstr "z.B. 10115" 34 35 - #: letters/forms.py:35 36 - msgid "Comma-separated tags (e.g., \"climate, transport, education\")" 37 - msgstr "Komma-getrennte Schlagwรถrter (z.B. \"Klima, Verkehr, Bildung\")" 38 39 - #: letters/forms.py:36 40 - msgid "climate, transport, education" 41 - msgstr "Klima, Verkehr, Bildung" 42 43 - #: letters/forms.py:43 44 msgid "Title" 45 msgstr "Titel" 46 47 - #: letters/forms.py:44 48 msgid "Letter Body" 49 msgstr "Brieftext" 50 51 - #: letters/forms.py:45 52 - #, fuzzy 53 - #| msgid "To Representative:" 54 msgid "To Representative" 55 - msgstr "An Abgeordnete:" 56 57 - #: letters/forms.py:48 58 msgid "Describe your concern briefly" 59 msgstr "Beschreiben Sie Ihr Anliegen kurz" 60 61 - #: letters/forms.py:49 62 - msgid "Write your letter here" 63 - msgstr "Schreiben Sie hier Ihren Brief" 64 65 - #: letters/forms.py:52 66 msgid "Letter title" 67 msgstr "Brieftitel" 68 69 - #: letters/forms.py:56 70 msgid "Write your letter here..." 71 msgstr "Schreiben Sie hier Ihren Brief..." 72 73 - #: letters/forms.py:156 74 msgid "Comment (optional)" 75 msgstr "Kommentar (optional)" 76 77 - #: letters/forms.py:159 78 msgid "Add a personal note to your signature" 79 msgstr "Fรผgen Sie Ihrer Unterschrift eine persรถnliche Notiz hinzu" 80 81 - #: letters/forms.py:165 82 msgid "Optional: Add your comment..." 83 msgstr "Optional: Fรผgen Sie Ihren Kommentar hinzu..." 84 85 - #: letters/forms.py:177 86 msgid "Reason" 87 msgstr "Grund" 88 89 - #: letters/forms.py:178 90 msgid "Description" 91 msgstr "Beschreibung" 92 93 - #: letters/forms.py:181 94 msgid "Please provide details about why you are reporting this letter" 95 msgstr "Bitte geben Sie Details an, warum Sie diesen Brief melden" 96 97 - #: letters/forms.py:188 98 msgid "Please describe the issue..." 99 msgstr "Bitte beschreiben Sie das Problem..." 100 101 - #: letters/forms.py:198 letters/templates/letters/letter_list.html:19 102 msgid "Search" 103 msgstr "Suchen" 104 105 - #: letters/forms.py:201 letters/templates/letters/letter_list.html:18 106 msgid "Search letters..." 107 msgstr "Briefe suchen..." 108 109 - #: letters/forms.py:207 110 msgid "Tag" 111 msgstr "Schlagwort" 112 113 - #: letters/forms.py:210 114 msgid "Filter by tag..." 115 msgstr "Nach Schlagwort filtern..." 116 117 # Model choices - Constituency 118 - #: letters/models.py:13 119 msgid "European Union" 120 msgstr "Europรคische Union" 121 122 - #: letters/models.py:14 123 msgid "Federal" 124 msgstr "Bund" 125 126 - #: letters/models.py:15 127 msgid "State" 128 msgstr "Land" 129 130 - #: letters/models.py:16 131 msgid "Local" 132 msgstr "Kommune" 133 134 - #: letters/models.py:19 135 msgid "Name of the parliament" 136 - msgstr "" 137 138 - #: letters/models.py:23 139 msgid "e.g., 'Bundestag', 'Bayerischer Landtag', 'Gemeinderat Mรผnchen'" 140 - msgstr "" 141 142 - #: letters/models.py:27 143 msgid "Geographic identifier (state code, municipality code, etc.)" 144 - msgstr "" 145 146 - #: letters/models.py:35 147 msgid "For hierarchical relationships (e.g., local within state)" 148 - msgstr "" 149 150 - #: letters/models.py:44 151 msgid "Parliament" 152 - msgstr "" 153 154 - #: letters/models.py:45 155 msgid "Parliaments" 156 - msgstr "" 157 - 158 - #: letters/models.py:80 159 - #, fuzzy 160 - #| msgid "Federal" 161 - msgid "Federal district" 162 - msgstr "Bund" 163 - 164 - #: letters/models.py:81 165 - msgid "State district" 166 - msgstr "" 167 - 168 - #: letters/models.py:82 169 - msgid "Regional district" 170 - msgstr "" 171 172 - #: letters/models.py:114 173 msgid "Federal electoral district" 174 - msgstr "" 175 176 - #: letters/models.py:115 177 msgid "Bundestag state list" 178 - msgstr "" 179 180 - #: letters/models.py:116 181 msgid "Bundestag federal list" 182 - msgstr "" 183 184 - #: letters/models.py:117 185 msgid "State electoral district" 186 - msgstr "" 187 188 - #: letters/models.py:118 189 msgid "State regional list" 190 - msgstr "" 191 192 - #: letters/models.py:119 193 msgid "State wide list" 194 - msgstr "" 195 196 - #: letters/models.py:120 197 msgid "EU at large" 198 - msgstr "" 199 200 - #: letters/models.py:153 201 msgid "Direct mandate" 202 - msgstr "" 203 204 - #: letters/models.py:154 205 msgid "State list mandate" 206 - msgstr "" 207 208 - #: letters/models.py:155 209 msgid "State regional list mandate" 210 - msgstr "" 211 212 - #: letters/models.py:156 213 msgid "Federal list mandate" 214 - msgstr "" 215 216 - #: letters/models.py:157 217 msgid "EU list mandate" 218 - msgstr "" 219 220 # Model choices - Letter 221 - #: letters/models.py:422 222 msgid "Draft" 223 msgstr "Entwurf" 224 225 - #: letters/models.py:423 226 msgid "Published" 227 msgstr "Verรถffentlicht" 228 229 - #: letters/models.py:424 230 msgid "Flagged for Review" 231 msgstr "Zur รœberprรผfung markiert" 232 233 - #: letters/models.py:425 234 msgid "Removed" 235 msgstr "Entfernt" 236 237 - #: letters/services.py:1149 238 #, python-format 239 msgid "Detected policy area: %(topic)s." 240 - msgstr "" 241 242 - #: letters/services.py:1154 243 - #, fuzzy, python-format 244 - #| msgid "Use your PLZ to narrow down representatives from your constituency." 245 msgid "Prioritising representatives for %(constituencies)s." 246 - msgstr "" 247 - "Verwenden Sie Ihre PLZ, um Abgeordnete aus Ihrem Wahlkreis einzugrenzen." 248 249 - #: letters/services.py:1159 250 #, python-format 251 msgid "Filtering by state %(state)s." 252 - msgstr "" 253 254 - #: letters/services.py:1163 255 #, python-format 256 msgid "" 257 "Postal code %(plz)s had no direct match; showing broader representatives." 258 msgstr "" 259 260 - #: letters/services.py:1167 261 - #, fuzzy 262 - #| msgid "No external resources available for this representative." 263 msgid "Showing generally relevant representatives." 264 - msgstr "Keine externen Ressourcen fรผr diesen Abgeordneten verfรผgbar." 265 266 # Navigation 267 - #: letters/templates/letters/base.html:124 268 msgid "Letters" 269 msgstr "Briefe" 270 271 - #: letters/templates/letters/base.html:126 272 - #: letters/templates/letters/representative_detail.html:196 273 msgid "Write Letter" 274 msgstr "Brief schreiben" 275 276 - #: letters/templates/letters/base.html:127 277 #: letters/templates/letters/profile.html:4 278 msgid "Profile" 279 msgstr "Profil" 280 281 - #: letters/templates/letters/base.html:128 282 msgid "Logout" 283 msgstr "Abmelden" 284 285 - #: letters/templates/letters/base.html:130 286 - #: letters/templates/letters/letter_detail.html:46 287 - #: letters/templates/letters/letter_detail.html:80 288 #: letters/templates/letters/login.html:4 289 #: letters/templates/letters/login.html:8 290 #: letters/templates/letters/login.html:33 291 msgid "Login" 292 msgstr "Anmelden" 293 294 - #: letters/templates/letters/base.html:131 295 #: letters/templates/letters/register.html:4 296 #: letters/templates/letters/register.html:8 297 - #: letters/templates/letters/register.html:65 298 msgid "Register" 299 msgstr "Registrieren" 300 301 - #: letters/templates/letters/base.html:134 302 msgid "Admin" 303 msgstr "Admin" 304 305 # Footer 306 - #: letters/templates/letters/base.html:154 307 msgid "Empowering citizens to write to their representatives" 308 msgstr "Bรผrgern ermรถglichen, an ihre Abgeordneten zu schreiben" 309 310 #: letters/templates/letters/letter_detail.html:10 311 #: letters/templates/letters/partials/letter_card.html:5 312 msgid "By" 313 msgstr "Von" 314 315 - #: letters/templates/letters/letter_detail.html:11 316 - #: letters/templates/letters/partials/letter_card.html:6 317 - #: letters/templates/letters/profile.html:68 318 - msgid "To" 319 - msgstr "An" 320 - 321 - #: letters/templates/letters/letter_detail.html:28 322 #, python-format 323 msgid "Signatures (%(counter)s)" 324 msgid_plural "Signatures (%(counter)s)" 325 msgstr[0] "Unterschrift (%(counter)s)" 326 msgstr[1] "Unterschriften (%(counter)s)" 327 328 - #: letters/templates/letters/letter_detail.html:30 329 - #, fuzzy, python-format 330 - #| msgid "<strong>%(counter)s</strong> constituent of %(constituency)s" 331 - #| msgid_plural "<strong>%(counter)s</strong> constituents of %(constituency)s" 332 msgid "%(counter)s constituent of %(constituency_name)s" 333 msgid_plural "%(counter)s constituents of %(constituency_name)s" 334 msgstr[0] "%(counter)s Wรคhler aus %(constituency_name)s" 335 msgstr[1] "%(counter)s Wรคhler aus %(constituency_name)s" 336 337 - #: letters/templates/letters/letter_detail.html:31 338 - #, fuzzy, python-format 339 - #| msgid "%(counter)s verified" 340 msgid "%(counter)s other verified" 341 msgid_plural "%(counter)s other verified" 342 - msgstr[0] "%(counter)s verifiziert" 343 - msgstr[1] "%(counter)s verifiziert" 344 345 - #: letters/templates/letters/letter_detail.html:32 346 #, python-format 347 msgid "%(counter)s unverified" 348 msgid_plural "%(counter)s unverified" 349 msgstr[0] "%(counter)s nicht verifiziert" 350 msgstr[1] "%(counter)s nicht verifiziert" 351 352 - #: letters/templates/letters/letter_detail.html:40 353 msgid "Sign this letter" 354 msgstr "Brief unterzeichnen" 355 356 - #: letters/templates/letters/letter_detail.html:43 357 msgid "You have signed this letter" 358 msgstr "Sie haben diesen Brief unterzeichnet" 359 360 - #: letters/templates/letters/letter_detail.html:46 361 - #, fuzzy 362 - #| msgid "Login to sign this letter" 363 msgid "to sign this letter" 364 - msgstr "Anmelden um zu unterzeichnen" 365 366 - #: letters/templates/letters/letter_detail.html:69 367 msgid "No signatures yet. Be the first to sign!" 368 msgstr "Noch keine Unterschriften. Seien Sie der Erste!" 369 370 - #: letters/templates/letters/letter_detail.html:74 371 - #: letters/templates/letters/letter_detail.html:78 372 msgid "Report this letter" 373 msgstr "Brief melden" 374 375 - #: letters/templates/letters/letter_detail.html:75 376 msgid "If you believe this letter violates our guidelines, please report it." 377 - msgstr "" 378 379 - #: letters/templates/letters/letter_detail.html:80 380 - #, fuzzy 381 - #| msgid "Report this letter" 382 msgid "to report this letter" 383 - msgstr "Brief melden" 384 385 - #: letters/templates/letters/letter_detail.html:85 386 msgid "Back to all letters" 387 msgstr "Zurรผck zu allen Briefen" 388 389 #: letters/templates/letters/letter_form.html:4 390 - #: letters/templates/letters/representative_detail.html:190 391 msgid "Write a Letter" 392 msgstr "Brief schreiben" 393 394 # Letter form 395 - #: letters/templates/letters/letter_form.html:11 396 msgid "Write an Open Letter" 397 msgstr "Einen offenen Brief schreiben" 398 399 - #: letters/templates/letters/letter_form.html:13 400 msgid "" 401 - "Write an open letter to a German political representative. Your letter will " 402 - "be published publicly and others can sign it." 403 msgstr "" 404 - "Schreiben Sie einen offenen Brief an einen deutschen Abgeordneten. Ihr Brief " 405 - "wird รถffentlich verรถffentlicht und andere kรถnnen ihn unterzeichnen." 406 407 #: letters/templates/letters/letter_form.html:20 408 msgid "Title:" 409 msgstr "Titel:" 410 411 - #: letters/templates/letters/letter_form.html:26 412 msgid "" 413 - "Describe your concern. We'll suggest the right representatives based on your " 414 - "title." 415 - msgstr "" 416 - "Beschreiben Sie Ihr Anliegen. Wir schlagen Ihnen passende Abgeordnete " 417 - "basierend auf Ihrem Titel vor." 418 - 419 - #: letters/templates/letters/letter_form.html:31 420 - msgid "Your postal code (PLZ):" 421 - msgstr "Ihre Postleitzahl (PLZ):" 422 - 423 - #: letters/templates/letters/letter_form.html:37 424 - msgid "We'll use this PLZ to highlight representatives from your constituency." 425 msgstr "" 426 - "Wir verwenden diese PLZ, um Abgeordnete aus Ihrem Wahlkreis hervorzuheben." 427 428 - #: letters/templates/letters/letter_form.html:45 429 msgid "To Representative:" 430 msgstr "An Abgeordnete:" 431 432 - #: letters/templates/letters/letter_form.html:51 433 - msgid "Or select from suggestions on the right โ†’" 434 - msgstr "Oder wรคhlen Sie aus den Vorschlรคgen rechts โ†’" 435 436 - #: letters/templates/letters/letter_form.html:56 437 msgid "Letter Body:" 438 msgstr "Brieftext:" 439 440 - #: letters/templates/letters/letter_form.html:64 441 - msgid "Tags (optional):" 442 - msgstr "Schlagwรถrter (optional):" 443 - 444 - #: letters/templates/letters/letter_form.html:71 445 msgid "Publish Letter" 446 msgstr "Brief verรถffentlichen" 447 448 - #: letters/templates/letters/letter_form.html:72 449 - msgid "Cancel" 450 - msgstr "Abbrechen" 451 - 452 - #: letters/templates/letters/letter_form.html:81 453 msgid "Smart Suggestions" 454 msgstr "Intelligente Vorschlรคge" 455 456 - #: letters/templates/letters/letter_form.html:83 457 - #, fuzzy 458 - #| msgid "" 459 - #| "Type your letter title and we'll help you find the right representative " 460 - #| "and show similar letters." 461 msgid "" 462 - "Add a title and your PLZ to see tailored representatives, tags, and similar " 463 - "letters." 464 msgstr "" 465 - "Geben Sie Ihren Brieftitel ein und wir helfen Ihnen, den richtigen " 466 - "Abgeordneten zu finden und zeigen รคhnliche Briefe." 467 468 - #: letters/templates/letters/letter_form.html:98 469 msgid "Loading..." 470 msgstr "Lรคdt..." 471 472 - #: letters/templates/letters/letter_form.html:100 473 msgid "Analyzing your title..." 474 msgstr "Analysiere Ihren Titel..." 475 ··· 510 msgid "Popular tags:" 511 msgstr "Beliebte Schlagwรถrter:" 512 513 # Login page 514 #: letters/templates/letters/login.html:14 515 - #: letters/templates/letters/register.html:14 516 msgid "Username:" 517 msgstr "Benutzername:" 518 519 #: letters/templates/letters/login.html:22 520 - #: letters/templates/letters/register.html:46 521 msgid "Password:" 522 msgstr "Passwort:" 523 524 #: letters/templates/letters/login.html:37 525 msgid "Don't have an account?" 526 msgstr "Noch kein Konto?" 527 528 - #: letters/templates/letters/login.html:37 529 msgid "Register here" 530 msgstr "Hier registrieren" 531 532 # Plurals for signatures 533 #: letters/templates/letters/partials/letter_card.html:20 534 - #: letters/templates/letters/partials/suggestions.html:126 535 #, python-format 536 msgid "%(counter)s signature" 537 msgid_plural "%(counter)s signatures" ··· 539 msgstr[1] "%(counter)s Unterschriften" 540 541 #: letters/templates/letters/partials/letter_card.html:20 542 - #, fuzzy, python-format 543 - #| msgid "%(counter)s verified" 544 msgid "%(counter)s verified" 545 msgid_plural "%(counter)s verified" 546 msgstr[0] "%(counter)s verifiziert" 547 msgstr[1] "%(counter)s verifiziert" 548 549 # Suggestions partial 550 - #: letters/templates/letters/partials/suggestions.html:11 551 msgid "Our Interpretation" 552 msgstr "Unsere Interpretation" 553 554 - #: letters/templates/letters/partials/suggestions.html:16 555 msgid "Topic:" 556 msgstr "Thema:" 557 558 - #: letters/templates/letters/partials/suggestions.html:23 559 msgid "No specific policy area detected. Try adding more keywords." 560 msgstr "" 561 "Kein spezifischer Politikbereich erkannt. Versuchen Sie, mehr " 562 "Schlรผsselwรถrter hinzuzufรผgen." 563 564 - #: letters/templates/letters/partials/suggestions.html:31 565 - msgid "Suggested Representatives" 566 - msgstr "Vorgeschlagene Abgeordnete" 567 568 - #: letters/templates/letters/partials/suggestions.html:37 569 #, python-format 570 msgid "" 571 - "Based on the topic \"%(topic)s\", we suggest contacting representatives from " 572 - "the %(level)s:" 573 msgstr "" 574 - "Basierend auf dem Thema \"%(topic)s\" empfehlen wir, Abgeordnete des " 575 - "%(level)s zu kontaktieren:" 576 577 - #: letters/templates/letters/partials/suggestions.html:51 578 - msgid "Constituency:" 579 - msgstr "Wahlkreis:" 580 - 581 - #: letters/templates/letters/partials/suggestions.html:57 582 msgid "of" 583 msgstr "von" 584 585 - #: letters/templates/letters/partials/suggestions.html:61 586 - msgid "View profile" 587 - msgstr "Profil ansehen" 588 589 - #: letters/templates/letters/partials/suggestions.html:66 590 - msgid "Select" 591 - msgstr "Auswรคhlen" 592 - 593 - #: letters/templates/letters/partials/suggestions.html:73 594 msgid "" 595 "No representatives found. Representatives may need to be synced for this " 596 "governmental level." ··· 598 "Keine Abgeordneten gefunden. Abgeordnete mรผssen mรถglicherweise fรผr diese " 599 "Verwaltungsebene synchronisiert werden." 600 601 - #: letters/templates/letters/partials/suggestions.html:82 602 - #, fuzzy 603 - #| msgid "Suggested Representatives" 604 - msgid "Suggested Tags" 605 - msgstr "Vorgeschlagene Abgeordnete" 606 607 - #: letters/templates/letters/partials/suggestions.html:85 608 - msgid "Click a tag to add it to your letter." 609 msgstr "" 610 611 - #: letters/templates/letters/partials/suggestions.html:101 612 - msgid "Related Keywords" 613 - msgstr "Verwandte Schlagwรถrter" 614 615 - #: letters/templates/letters/partials/suggestions.html:115 616 - msgid "Similar Letters" 617 - msgstr "ร„hnliche Briefe" 618 619 - #: letters/templates/letters/partials/suggestions.html:118 620 - msgid "Others have written about similar topics:" 621 - msgstr "Andere haben รผber รคhnliche Themen geschrieben:" 622 623 - #: letters/templates/letters/partials/suggestions.html:129 624 - #, fuzzy 625 - #| msgid "To" 626 - msgid "To:" 627 - msgstr "An" 628 629 - #: letters/templates/letters/partials/suggestions.html:131 630 - msgid "by" 631 msgstr "" 632 633 - #: letters/templates/letters/partials/suggestions.html:163 634 - msgid "Selected:" 635 - msgstr "Ausgewรคhlt:" 636 637 # Profile page 638 - #: letters/templates/letters/profile.html:8 639 #, python-format 640 msgid "%(username)s's Profile" 641 msgstr "Profil von %(username)s" 642 643 - #: letters/templates/letters/profile.html:11 644 - msgid "Identity Verification" 645 - msgstr "Identitรคtsverifizierung" 646 647 - #: letters/templates/letters/profile.html:15 648 - msgid "Identity Verified" 649 - msgstr "Identitรคt verifiziert" 650 - 651 - #: letters/templates/letters/profile.html:17 652 - msgid "Your signatures will be marked as verified constituent signatures." 653 - msgstr "" 654 - "Ihre Unterschriften werden als verifizierte Wรคhlerunterschriften markiert." 655 656 #: letters/templates/letters/profile.html:21 657 - #: letters/templates/letters/representative_detail.html:23 658 - msgid "Parliament:" 659 msgstr "" 660 661 - #: letters/templates/letters/profile.html:27 662 - msgid "Verification Pending" 663 - msgstr "Verifizierung ausstehend" 664 665 - #: letters/templates/letters/profile.html:29 666 msgid "Your verification is being processed." 667 msgstr "Ihre Verifizierung wird bearbeitet." 668 669 - #: letters/templates/letters/profile.html:31 670 msgid "Complete Verification (Stub)" 671 msgstr "Verifizierung abschlieรŸen (Stub)" 672 673 - #: letters/templates/letters/profile.html:35 674 - msgid "Verification Failed" 675 - msgstr "Verifizierung fehlgeschlagen" 676 677 - #: letters/templates/letters/profile.html:37 678 - msgid "Please try again or contact support." 679 - msgstr "Bitte versuchen Sie es erneut oder kontaktieren Sie den Support." 680 681 - #: letters/templates/letters/profile.html:43 682 msgid "" 683 - "Verify your identity to prove you're a constituent of the representatives " 684 - "you write to. Verified signatures carry more weight!" 685 msgstr "" 686 - "Verifizieren Sie Ihre Identitรคt, um zu beweisen, dass Sie Wรคhler der " 687 - "Abgeordneten sind, an die Sie schreiben. Verifizierte Unterschriften haben " 688 - "mehr Gewicht!" 689 690 - #: letters/templates/letters/profile.html:45 691 - msgid "Start Verification" 692 - msgstr "Verifizierung starten" 693 694 - #: letters/templates/letters/profile.html:51 695 msgid "Your Letters" 696 msgstr "Ihre Briefe" 697 698 - #: letters/templates/letters/profile.html:57 699 msgid "You haven't written any letters yet." 700 msgstr "Sie haben noch keine Briefe geschrieben." 701 702 - #: letters/templates/letters/profile.html:57 703 msgid "Write one now!" 704 msgstr "Schreiben Sie jetzt einen!" 705 706 - #: letters/templates/letters/profile.html:62 707 msgid "Letters You've Signed" 708 msgstr "Briefe, die Sie unterzeichnet haben" 709 710 - # Letter detail page 711 - #: letters/templates/letters/profile.html:68 712 - msgid "Signed on" 713 - msgstr "Unterzeichnet am" 714 - 715 - #: letters/templates/letters/profile.html:71 716 - msgid "Your comment:" 717 - msgstr "Ihr Kommentar:" 718 - 719 - #: letters/templates/letters/profile.html:76 720 msgid "You haven't signed any letters yet." 721 msgstr "Sie haben noch keine Briefe unterzeichnet." 722 723 - #: letters/templates/letters/profile.html:76 724 msgid "Browse letters" 725 msgstr "Briefe durchsuchen" 726 727 - #: letters/templates/letters/register.html:22 728 - #: letters/templates/letters/representative_detail.html:56 729 msgid "Email:" 730 msgstr "E-Mail:" 731 732 # Register page 733 - #: letters/templates/letters/register.html:30 734 msgid "First Name (optional):" 735 msgstr "Vorname (optional):" 736 737 - #: letters/templates/letters/register.html:38 738 msgid "Last Name (optional):" 739 msgstr "Nachname (optional):" 740 741 - #: letters/templates/letters/register.html:54 742 msgid "Confirm Password:" 743 msgstr "Passwort bestรคtigen:" 744 745 - #: letters/templates/letters/register.html:69 746 msgid "Already have an account?" 747 msgstr "Bereits ein Konto?" 748 749 - #: letters/templates/letters/register.html:69 750 msgid "Login here" 751 msgstr "Hier anmelden" 752 753 - # Representative detail 754 #: letters/templates/letters/representative_detail.html:19 755 - msgid "Party:" 756 - msgstr "Partei:" 757 - 758 - #: letters/templates/letters/representative_detail.html:27 759 - msgid "Legislative Body:" 760 - msgstr "Parlament:" 761 - 762 - #: letters/templates/letters/representative_detail.html:32 763 - msgid "Role:" 764 - msgstr "Rolle:" 765 - 766 - #: letters/templates/letters/representative_detail.html:38 767 - msgid "Term:" 768 - msgstr "Amtszeit:" 769 - 770 - #: letters/templates/letters/representative_detail.html:43 771 - msgid "Present" 772 - msgstr "Heute" 773 - 774 - #: letters/templates/letters/representative_detail.html:47 775 - msgid "Status:" 776 - msgstr "Status:" 777 - 778 - #: letters/templates/letters/representative_detail.html:49 779 - #: letters/templates/letters/representative_detail.html:98 780 - msgid "Active" 781 - msgstr "Aktiv" 782 783 - #: letters/templates/letters/representative_detail.html:51 784 - msgid "Inactive" 785 - msgstr "Inaktiv" 786 - 787 - #: letters/templates/letters/representative_detail.html:62 788 - msgid "Website:" 789 - msgstr "Webseite:" 790 - 791 - #: letters/templates/letters/representative_detail.html:75 792 msgid "Committee Memberships" 793 msgstr "Ausschussmitgliedschaften" 794 795 - #: letters/templates/letters/representative_detail.html:113 796 - msgid "Policy Competences" 797 - msgstr "Politische Kompetenzen" 798 - 799 - #: letters/templates/letters/representative_detail.html:117 800 - msgid "" 801 - "Based on committee memberships, this representative works on the following " 802 - "policy areas:" 803 - msgstr "" 804 - "Basierend auf Ausschussmitgliedschaften arbeitet dieser Abgeordnete in " 805 - "folgenden Politikbereichen:" 806 - 807 - #: letters/templates/letters/representative_detail.html:134 808 msgid "Open Letters" 809 msgstr "Offene Briefe" 810 811 - #: letters/templates/letters/representative_detail.html:142 812 msgid "No letters have been written to this representative yet." 813 msgstr "An diesen Abgeordneten wurden noch keine Briefe geschrieben." 814 815 - #: letters/templates/letters/representative_detail.html:144 816 msgid "Write the First Letter" 817 msgstr "Ersten Brief schreiben" 818 819 - #: letters/templates/letters/representative_detail.html:156 820 msgid "External Resources" 821 msgstr "Externe Ressourcen" 822 823 - #: letters/templates/letters/representative_detail.html:161 824 msgid "Abgeordnetenwatch Profile" 825 msgstr "Abgeordnetenwatch-Profil" 826 827 - #: letters/templates/letters/representative_detail.html:163 828 msgid "" 829 "View voting record, questions, and detailed profile on Abgeordnetenwatch.de" 830 msgstr "" 831 "Abstimmungsverhalten, Fragen und detailliertes Profil auf Abgeordnetenwatch." 832 "de ansehen" 833 834 - #: letters/templates/letters/representative_detail.html:166 835 - msgid "View on Abgeordnetenwatch" 836 - msgstr "Auf Abgeordnetenwatch ansehen" 837 838 - #: letters/templates/letters/representative_detail.html:173 839 - msgid "Wikipedia Article (German)" 840 - msgstr "Wikipedia-Artikel (Deutsch)" 841 - 842 - #: letters/templates/letters/representative_detail.html:175 843 msgid "Read more about this representative on Wikipedia" 844 msgstr "Mehr รผber diesen Abgeordneten auf Wikipedia lesen" 845 846 - #: letters/templates/letters/representative_detail.html:178 847 msgid "View on Wikipedia" 848 msgstr "Auf Wikipedia ansehen" 849 850 - #: letters/templates/letters/representative_detail.html:184 851 msgid "No external resources available for this representative." 852 msgstr "Keine externen Ressourcen fรผr diesen Abgeordneten verfรผgbar." 853 854 - #: letters/templates/letters/representative_detail.html:192 855 #, python-format 856 msgid "Start a new open letter to %(name)s" 857 msgstr "Einen neuen offenen Brief an %(name)s beginnen" 858 859 - #: letters/templates/letters/representative_detail.html:200 860 msgid "Login to Write Letter" 861 msgstr "Anmelden um Brief zu schreiben" 862 863 # Flash messages 864 - #: letters/views.py:158 865 msgid "Your letter has been published and your signature has been added!" 866 - msgstr "" 867 - "Ihr Brief wurde verรถffentlicht und Ihre Unterschrift wurde hinzugefรผgt!" 868 869 - #: letters/views.py:171 870 msgid "You have already signed this letter." 871 msgstr "Sie haben diesen Brief bereits unterzeichnet." 872 873 - #: letters/views.py:181 874 msgid "Your signature has been added!" 875 msgstr "Ihre Unterschrift wurde hinzugefรผgt!" 876 877 - #: letters/views.py:199 878 msgid "Thank you for your report. Our team will review it." 879 msgstr "Vielen Dank fรผr Ihre Meldung. Unser Team wird sie รผberprรผfen." 880 881 - #: letters/views.py:226 882 - #, python-format 883 - msgid "Welcome, %(username)s! Your account has been created." 884 - msgstr "Willkommen, %(username)s! Ihr Konto wurde erstellt." 885 886 - #~ msgid "Previous" 887 - #~ msgstr "Zurรผck" 888 889 - #~ msgid "Next" 890 - #~ msgstr "Weiter" 891 892 #, python-format 893 - #~ msgid "Page %(page)s of %(total)s" 894 - #~ msgstr "Seite %(page)s von %(total)s" 895 896 - #~ msgid "No letters found." 897 - #~ msgstr "Keine Briefe gefunden." 898 899 - #~ msgid "Be the first to write one!" 900 - #~ msgstr "Schreiben Sie den ersten!" 901 902 #, python-format 903 #~ msgid "<strong>%(counter)s</strong> other verified" ··· 915 916 #~ msgid "Committee Memberships:" 917 #~ msgstr "Ausschussmitgliedschaften:" 918 - 919 - #~ msgid "Policy Areas:" 920 - #~ msgstr "Politikbereiche:" 921 922 # Services explanations 923 #~ msgid "No matching policy areas found. Please try different keywords."
··· 6 msgstr "" 7 "Project-Id-Version: WriteThem.eu\n" 8 "Report-Msgid-Bugs-To: \n" 9 + "POT-Creation-Date: 2025-10-15 00:27+0200\n" 10 "PO-Revision-Date: 2025-01-10 12:00+0100\n" 11 "Last-Translator: \n" 12 "Language-Team: German\n" ··· 16 "Content-Transfer-Encoding: 8bit\n" 17 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 19 + #: letters/admin.py:75 20 + msgid "Mandate Details" 21 + msgstr "Mandatsdetails" 22 23 + #: letters/admin.py:78 24 + msgid "Focus Areas" 25 + msgstr "Schwerpunktbereiche" 26 27 + #: letters/admin.py:81 letters/admin.py:95 28 + msgid "Photo" 29 + msgstr "Foto" 30 31 + #: letters/admin.py:85 32 + msgid "Metadata" 33 + msgstr "Metadaten" 34 35 + #: letters/admin.py:94 36 + msgid "No photo" 37 + msgstr "Kein Foto" 38 + 39 + #: letters/admin.py:167 40 + msgid "Topic Areas" 41 + msgstr "Themenbereiche" 42 + 43 + #: letters/forms.py:25 44 + msgid "" 45 + "An account with this email already exists. If you registered before, please " 46 + "check your inbox for the activation link or reset your password." 47 + msgstr "" 48 + "Ein Konto mit dieser E-Mail-Adresse existiert bereits. Falls Sie sich zuvor " 49 + "registriert haben, prรผfen Sie Ihren Posteingang auf den Aktivierungslink oder " 50 + "setzen Sie Ihr Passwort zurรผck." 51 52 + #: letters/forms.py:37 53 msgid "Title" 54 msgstr "Titel" 55 56 + #: letters/forms.py:38 57 msgid "Letter Body" 58 msgstr "Brieftext" 59 60 + #: letters/forms.py:39 61 msgid "To Representative" 62 + msgstr "An Abgeordnete" 63 64 + #: letters/forms.py:42 65 msgid "Describe your concern briefly" 66 msgstr "Beschreiben Sie Ihr Anliegen kurz" 67 68 + #: letters/forms.py:43 69 + msgid "" 70 + "Write your letter here. Markdown formatting (e.g. **bold**, _italic_) is " 71 + "supported." 72 + msgstr "" 73 + "Schreiben Sie hier Ihren Brief. Markdown-Formatierung (z.B. **fett**, _kursiv_) " 74 + "wird unterstรผtzt." 75 76 + #: letters/forms.py:46 77 msgid "Letter title" 78 msgstr "Brieftitel" 79 80 + #: letters/forms.py:50 81 msgid "Write your letter here..." 82 msgstr "Schreiben Sie hier Ihren Brief..." 83 84 + #: letters/forms.py:110 85 msgid "Comment (optional)" 86 msgstr "Kommentar (optional)" 87 88 + #: letters/forms.py:113 89 msgid "Add a personal note to your signature" 90 msgstr "Fรผgen Sie Ihrer Unterschrift eine persรถnliche Notiz hinzu" 91 92 + #: letters/forms.py:119 93 msgid "Optional: Add your comment..." 94 msgstr "Optional: Fรผgen Sie Ihren Kommentar hinzu..." 95 96 + #: letters/forms.py:131 97 msgid "Reason" 98 msgstr "Grund" 99 100 + #: letters/forms.py:132 101 msgid "Description" 102 msgstr "Beschreibung" 103 104 + #: letters/forms.py:135 105 msgid "Please provide details about why you are reporting this letter" 106 msgstr "Bitte geben Sie Details an, warum Sie diesen Brief melden" 107 108 + #: letters/forms.py:142 109 msgid "Please describe the issue..." 110 msgstr "Bitte beschreiben Sie das Problem..." 111 112 + #: letters/forms.py:152 letters/templates/letters/letter_list.html:19 113 msgid "Search" 114 msgstr "Suchen" 115 116 + #: letters/forms.py:155 letters/templates/letters/letter_list.html:18 117 msgid "Search letters..." 118 msgstr "Briefe suchen..." 119 120 + #: letters/forms.py:161 121 msgid "Tag" 122 msgstr "Schlagwort" 123 124 + #: letters/forms.py:164 125 msgid "Filter by tag..." 126 msgstr "Nach Schlagwort filtern..." 127 128 + #: letters/forms.py:180 129 + msgid "Bundestag constituency" 130 + msgstr "Bundestags-Wahlkreis" 131 + 132 + #: letters/forms.py:181 133 + msgid "Pick your Bundestag direct mandate constituency (Wahlkreis)." 134 + msgstr "Wรคhlen Sie Ihren Bundestags-Direktwahlkreis (Wahlkreis)." 135 + 136 + #: letters/forms.py:182 letters/forms.py:189 137 + msgid "Select constituency" 138 + msgstr "Wahlkreis auswรคhlen" 139 + 140 + #: letters/forms.py:187 141 + msgid "State parliament constituency" 142 + msgstr "Landtags-Wahlkreis" 143 + 144 + #: letters/forms.py:188 145 + msgid "Optionally pick your Landtag constituency if applicable." 146 + msgstr "Wรคhlen Sie optional Ihren Landtags-Wahlkreis, falls zutreffend." 147 + 148 + #: letters/forms.py:240 149 + msgid "Please select at least one constituency to save your profile." 150 + msgstr "Bitte wรคhlen Sie mindestens einen Wahlkreis aus, um Ihr Profil zu speichern." 151 + 152 + #: letters/forms.py:252 153 + msgid "StraรŸe und Hausnummer" 154 + msgstr "StraรŸe und Hausnummer" 155 + 156 + #: letters/forms.py:255 157 + msgid "z.B. Unter den Linden 77" 158 + msgstr "z.B. Unter den Linden 77" 159 + 160 + #: letters/forms.py:261 161 + msgid "Postleitzahl" 162 + msgstr "Postleitzahl" 163 + 164 + #: letters/forms.py:264 165 + msgid "z.B. 10117" 166 + msgstr "z.B. 10117" 167 + 168 + #: letters/forms.py:270 169 + msgid "Stadt" 170 + msgstr "Stadt" 171 + 172 + #: letters/forms.py:273 173 + msgid "z.B. Berlin" 174 + msgstr "z.B. Berlin" 175 + 176 + #: letters/forms.py:304 177 + msgid "" 178 + "Bitte geben Sie eine vollstรคndige Adresse ein (StraรŸe, PLZ und Stadt) oder " 179 + "lassen Sie alle Felder leer." 180 + msgstr "" 181 + "Bitte geben Sie eine vollstรคndige Adresse ein (StraรŸe, PLZ und Stadt) oder " 182 + "lassen Sie alle Felder leer." 183 + 184 # Model choices - Constituency 185 + #: letters/models.py:15 186 msgid "European Union" 187 msgstr "Europรคische Union" 188 189 + #: letters/models.py:16 190 msgid "Federal" 191 msgstr "Bund" 192 193 + #: letters/models.py:17 194 msgid "State" 195 msgstr "Land" 196 197 + #: letters/models.py:18 198 msgid "Local" 199 msgstr "Kommune" 200 201 + #: letters/models.py:21 202 msgid "Name of the parliament" 203 + msgstr "Name des Parlaments" 204 205 + #: letters/models.py:25 206 msgid "e.g., 'Bundestag', 'Bayerischer Landtag', 'Gemeinderat Mรผnchen'" 207 + msgstr "z.B. 'Bundestag', 'Bayerischer Landtag', 'Gemeinderat Mรผnchen'" 208 209 + #: letters/models.py:29 210 msgid "Geographic identifier (state code, municipality code, etc.)" 211 + msgstr "Geografische Kennung (Bundeslandkรผrzel, Gemeindeschlรผssel, etc.)" 212 213 + #: letters/models.py:37 214 msgid "For hierarchical relationships (e.g., local within state)" 215 + msgstr "Fรผr hierarchische Beziehungen (z.B. Kommune innerhalb eines Bundeslandes)" 216 + 217 + #: letters/models.py:43 letters/models.py:70 letters/models.py:105 218 + #: letters/models.py:165 letters/models.py:372 letters/models.py:421 219 + msgid "Last time this was synced from external API" 220 + msgstr "Zeitpunkt der letzten Synchronisierung von externer API" 221 222 + #: letters/models.py:47 letters/templates/letters/committee_detail.html:70 223 msgid "Parliament" 224 + msgstr "Parlament" 225 226 + #: letters/models.py:48 227 msgid "Parliaments" 228 + msgstr "Parlamente" 229 230 + #: letters/models.py:84 231 msgid "Federal electoral district" 232 + msgstr "Bundeswahlkreis" 233 234 + #: letters/models.py:85 235 msgid "Bundestag state list" 236 + msgstr "Bundestag-Landesliste" 237 238 + #: letters/models.py:86 239 msgid "Bundestag federal list" 240 + msgstr "Bundestag-Bundesliste" 241 242 + #: letters/models.py:87 243 msgid "State electoral district" 244 + msgstr "Landeswahlkreis" 245 246 + #: letters/models.py:88 247 msgid "State regional list" 248 + msgstr "Regionale Landesliste" 249 250 + #: letters/models.py:89 251 msgid "State wide list" 252 + msgstr "Landesweite Liste" 253 254 + #: letters/models.py:90 255 msgid "EU at large" 256 + msgstr "EU insgesamt" 257 258 + #: letters/models.py:119 259 msgid "Direct mandate" 260 + msgstr "Direktmandat" 261 262 + #: letters/models.py:120 263 msgid "State list mandate" 264 + msgstr "Landeslisten-Mandat" 265 266 + #: letters/models.py:121 267 msgid "State regional list mandate" 268 + msgstr "Regionales Landeslisten-Mandat" 269 270 + #: letters/models.py:122 271 msgid "Federal list mandate" 272 + msgstr "Bundeslisten-Mandat" 273 274 + #: letters/models.py:123 275 msgid "EU list mandate" 276 + msgstr "EU-Listen-Mandat" 277 278 # Model choices - Letter 279 + #: letters/models.py:444 280 msgid "Draft" 281 msgstr "Entwurf" 282 283 + #: letters/models.py:445 284 msgid "Published" 285 msgstr "Verรถffentlicht" 286 287 + #: letters/models.py:446 288 msgid "Flagged for Review" 289 msgstr "Zur รœberprรผfung markiert" 290 291 + #: letters/models.py:447 292 msgid "Removed" 293 msgstr "Entfernt" 294 295 + #: letters/models.py:487 296 + msgid "Deleted user" 297 + msgstr "Gelรถschter Benutzer" 298 + 299 + #: letters/services.py:2451 300 #, python-format 301 msgid "Detected policy area: %(topic)s." 302 + msgstr "Erkannter Politikbereich: %(topic)s." 303 304 + #: letters/services.py:2456 305 + #, python-format 306 msgid "Prioritising representatives for %(constituencies)s." 307 + msgstr "Priorisiere Abgeordnete fรผr %(constituencies)s." 308 309 + #: letters/services.py:2461 310 #, python-format 311 msgid "Filtering by state %(state)s." 312 + msgstr "Filtere nach Bundesland %(state)s." 313 314 + #: letters/services.py:2465 315 #, python-format 316 msgid "" 317 "Postal code %(plz)s had no direct match; showing broader representatives." 318 msgstr "" 319 + "Postleitzahl %(plz)s hatte keine direkte รœbereinstimmung; zeige allgemeinere Abgeordnete." 320 321 + #: letters/services.py:2469 322 msgid "Showing generally relevant representatives." 323 + msgstr "Zeige allgemein relevante Abgeordnete." 324 + 325 + #: letters/templates/letters/account_activation_invalid.html:4 326 + #: letters/templates/letters/account_activation_invalid.html:8 327 + msgid "Activation link invalid" 328 + msgstr "Aktivierungslink ungรผltig" 329 + 330 + #: letters/templates/letters/account_activation_invalid.html:10 331 + msgid "" 332 + "We could not verify your activation link. It may have already been used or " 333 + "expired." 334 + msgstr "" 335 + "Wir konnten Ihren Aktivierungslink nicht verifizieren. Er wurde mรถglicherweise bereits " 336 + "verwendet oder ist abgelaufen." 337 + 338 + #: letters/templates/letters/account_activation_invalid.html:13 339 + msgid "" 340 + "If you still cannot access your account, try registering again or contact " 341 + "support." 342 + msgstr "" 343 + "Falls Sie weiterhin nicht auf Ihr Konto zugreifen kรถnnen, versuchen Sie sich erneut zu " 344 + "registrieren oder kontaktieren Sie den Support." 345 + 346 + #: letters/templates/letters/account_activation_invalid.html:15 347 + msgid "Register again" 348 + msgstr "Erneut registrieren" 349 + 350 + #: letters/templates/letters/account_activation_sent.html:4 351 + msgid "Activate your account" 352 + msgstr "Aktivieren Sie Ihr Konto" 353 + 354 + #: letters/templates/letters/account_activation_sent.html:8 355 + #: letters/templates/letters/password_reset_done.html:8 356 + msgid "Check your inbox" 357 + msgstr "Prรผfen Sie Ihren Posteingang" 358 + 359 + #: letters/templates/letters/account_activation_sent.html:10 360 + msgid "" 361 + "We sent you an email with a confirmation link. Please click it to activate " 362 + "your account." 363 + msgstr "" 364 + "Wir haben Ihnen eine E-Mail mit einem Bestรคtigungslink gesendet. Bitte klicken Sie " 365 + "darauf, um Ihr Konto zu aktivieren." 366 + 367 + #: letters/templates/letters/account_activation_sent.html:13 368 + msgid "" 369 + "If you do not receive the email within a few minutes, check your spam folder " 370 + "or try registering again." 371 + msgstr "" 372 + "Falls Sie die E-Mail nicht innerhalb weniger Minuten erhalten, prรผfen Sie Ihren " 373 + "Spam-Ordner oder versuchen Sie sich erneut zu registrieren." 374 + 375 + #: letters/templates/letters/account_activation_sent.html:15 376 + msgid "Back to homepage" 377 + msgstr "Zurรผck zur Startseite" 378 + 379 + #: letters/templates/letters/account_delete_confirm.html:4 380 + #: letters/templates/letters/profile.html:142 381 + msgid "Delete account" 382 + msgstr "Konto lรถschen" 383 + 384 + #: letters/templates/letters/account_delete_confirm.html:8 385 + msgid "Delete your account" 386 + msgstr "Lรถschen Sie Ihr Konto" 387 + 388 + #: letters/templates/letters/account_delete_confirm.html:10 389 + msgid "" 390 + "Deleting your account will remove your personal data and signatures. Letters " 391 + "you have published stay online but are shown without your name." 392 + msgstr "" 393 + "Das Lรถschen Ihres Kontos entfernt Ihre persรถnlichen Daten und Unterschriften. Von Ihnen " 394 + "verรถffentlichte Briefe bleiben online, werden aber ohne Ihren Namen angezeigt." 395 + 396 + #: letters/templates/letters/account_delete_confirm.html:14 397 + msgid "Yes, delete my account" 398 + msgstr "Ja, mein Konto lรถschen" 399 + 400 + #: letters/templates/letters/account_delete_confirm.html:15 401 + #: letters/templates/letters/letter_form.html:61 402 + msgid "Cancel" 403 + msgstr "Abbrechen" 404 405 # Navigation 406 + #: letters/templates/letters/base.html:140 407 msgid "Letters" 408 msgstr "Briefe" 409 410 + #: letters/templates/letters/base.html:141 411 + msgid "Competencies" 412 + msgstr "Kompetenzen" 413 + 414 + #: letters/templates/letters/base.html:143 415 + #: letters/templates/letters/representative_detail.html:149 416 msgid "Write Letter" 417 msgstr "Brief schreiben" 418 419 + #: letters/templates/letters/base.html:144 420 #: letters/templates/letters/profile.html:4 421 msgid "Profile" 422 msgstr "Profil" 423 424 + #: letters/templates/letters/base.html:145 425 msgid "Logout" 426 msgstr "Abmelden" 427 428 + #: letters/templates/letters/base.html:147 429 + #: letters/templates/letters/letter_detail.html:47 430 + #: letters/templates/letters/letter_detail.html:81 431 #: letters/templates/letters/login.html:4 432 #: letters/templates/letters/login.html:8 433 #: letters/templates/letters/login.html:33 434 msgid "Login" 435 msgstr "Anmelden" 436 437 + #: letters/templates/letters/base.html:148 438 #: letters/templates/letters/register.html:4 439 #: letters/templates/letters/register.html:8 440 + #: letters/templates/letters/register.html:66 441 msgid "Register" 442 msgstr "Registrieren" 443 444 + #: letters/templates/letters/base.html:151 445 msgid "Admin" 446 msgstr "Admin" 447 448 + #: letters/templates/letters/base.html:157 449 + msgid "Select language" 450 + msgstr "Sprache auswรคhlen" 451 + 452 # Footer 453 + #: letters/templates/letters/base.html:182 454 msgid "Empowering citizens to write to their representatives" 455 msgstr "Bรผrgern ermรถglichen, an ihre Abgeordneten zu schreiben" 456 457 + #: letters/templates/letters/committee_detail.html:22 458 + msgid "Related Topics" 459 + msgstr "Verwandte Themen" 460 + 461 + #: letters/templates/letters/committee_detail.html:36 462 + msgid "Members" 463 + msgstr "Mitglieder" 464 + 465 + #: letters/templates/letters/committee_detail.html:46 466 + msgid "Role" 467 + msgstr "Rolle" 468 + 469 + #: letters/templates/letters/committee_detail.html:48 470 + #: letters/templates/letters/representative_detail.html:61 471 + msgid "Active" 472 + msgstr "Aktiv" 473 + 474 + #: letters/templates/letters/committee_detail.html:51 475 + #: letters/templates/letters/partials/representative_card.html:29 476 + msgid "Since" 477 + msgstr "Seit" 478 + 479 + #: letters/templates/letters/committee_detail.html:58 480 + msgid "No members recorded for this committee." 481 + msgstr "Fรผr diesen Ausschuss sind keine Mitglieder verzeichnet." 482 + 483 + #: letters/templates/letters/committee_detail.html:67 484 + msgid "Committee Info" 485 + msgstr "Ausschussinformationen" 486 + 487 + #: letters/templates/letters/committee_detail.html:71 488 + msgid "Term" 489 + msgstr "Amtszeit" 490 + 491 + #: letters/templates/letters/committee_detail.html:74 492 + #: letters/templates/letters/representative_detail.html:105 493 + msgid "View on Abgeordnetenwatch" 494 + msgstr "Auf Abgeordnetenwatch ansehen" 495 + 496 #: letters/templates/letters/letter_detail.html:10 497 #: letters/templates/letters/partials/letter_card.html:5 498 msgid "By" 499 msgstr "Von" 500 501 + #: letters/templates/letters/letter_detail.html:29 502 #, python-format 503 msgid "Signatures (%(counter)s)" 504 msgid_plural "Signatures (%(counter)s)" 505 msgstr[0] "Unterschrift (%(counter)s)" 506 msgstr[1] "Unterschriften (%(counter)s)" 507 508 + #: letters/templates/letters/letter_detail.html:31 509 + #, python-format 510 msgid "%(counter)s constituent of %(constituency_name)s" 511 msgid_plural "%(counter)s constituents of %(constituency_name)s" 512 msgstr[0] "%(counter)s Wรคhler aus %(constituency_name)s" 513 msgstr[1] "%(counter)s Wรคhler aus %(constituency_name)s" 514 515 + #: letters/templates/letters/letter_detail.html:32 516 + #, python-format 517 msgid "%(counter)s other verified" 518 msgid_plural "%(counter)s other verified" 519 + msgstr[0] "%(counter)s weitere verifiziert" 520 + msgstr[1] "%(counter)s weitere verifiziert" 521 522 + #: letters/templates/letters/letter_detail.html:33 523 #, python-format 524 msgid "%(counter)s unverified" 525 msgid_plural "%(counter)s unverified" 526 msgstr[0] "%(counter)s nicht verifiziert" 527 msgstr[1] "%(counter)s nicht verifiziert" 528 529 + #: letters/templates/letters/letter_detail.html:41 530 msgid "Sign this letter" 531 msgstr "Brief unterzeichnen" 532 533 + #: letters/templates/letters/letter_detail.html:44 534 msgid "You have signed this letter" 535 msgstr "Sie haben diesen Brief unterzeichnet" 536 537 + #: letters/templates/letters/letter_detail.html:47 538 msgid "to sign this letter" 539 + msgstr "um diesen Brief zu unterzeichnen" 540 541 + #: letters/templates/letters/letter_detail.html:58 542 + msgid "โœ“ Verified Constituent" 543 + msgstr "โœ“ Verifizierter Wรคhler" 544 + 545 + #: letters/templates/letters/letter_detail.html:60 546 + msgid "โœ“ Verified" 547 + msgstr "โœ“ Verifiziert" 548 + 549 + #: letters/templates/letters/letter_detail.html:70 550 msgid "No signatures yet. Be the first to sign!" 551 msgstr "Noch keine Unterschriften. Seien Sie der Erste!" 552 553 + #: letters/templates/letters/letter_detail.html:75 554 + #: letters/templates/letters/letter_detail.html:79 555 msgid "Report this letter" 556 msgstr "Brief melden" 557 558 + #: letters/templates/letters/letter_detail.html:76 559 msgid "If you believe this letter violates our guidelines, please report it." 560 + msgstr "Falls Sie glauben, dass dieser Brief gegen unsere Richtlinien verstรถรŸt, melden Sie ihn bitte." 561 562 + #: letters/templates/letters/letter_detail.html:81 563 msgid "to report this letter" 564 + msgstr "um diesen Brief zu melden" 565 566 + #: letters/templates/letters/letter_detail.html:86 567 msgid "Back to all letters" 568 msgstr "Zurรผck zu allen Briefen" 569 570 #: letters/templates/letters/letter_form.html:4 571 + #: letters/templates/letters/representative_detail.html:143 572 msgid "Write a Letter" 573 msgstr "Brief schreiben" 574 575 # Letter form 576 + #: letters/templates/letters/letter_form.html:10 577 msgid "Write an Open Letter" 578 msgstr "Einen offenen Brief schreiben" 579 580 + #: letters/templates/letters/letter_form.html:12 581 msgid "" 582 + "Write an open letter to a political representative. Your letter will be " 583 + "published publicly so others can read and sign it." 584 msgstr "" 585 + "Schreiben Sie einen offenen Brief an einen Abgeordneten. Ihr Brief wird " 586 + "รถffentlich verรถffentlicht, damit andere ihn lesen und unterzeichnen kรถnnen." 587 + 588 + #: letters/templates/letters/letter_form.html:16 589 + msgid "Before you write" 590 + msgstr "Bevor Sie schreiben" 591 + 592 + #: letters/templates/letters/letter_form.html:18 593 + msgid "Be thoughtful but feel free to be critical." 594 + msgstr "Seien Sie nachdenklich, aber scheuen Sie sich nicht, kritisch zu sein." 595 + 596 + #: letters/templates/letters/letter_form.html:19 597 + msgid "Representatives are humans tooโ€”stay respectful." 598 + msgstr "Abgeordnete sind auch Menschen โ€“ bleiben Sie respektvoll." 599 600 #: letters/templates/letters/letter_form.html:20 601 + msgid "Keep your arguments clear and concise." 602 + msgstr "Halten Sie Ihre Argumente klar und prรคgnant." 603 + 604 + #: letters/templates/letters/letter_form.html:21 605 + msgid "No insults or hate speech." 606 + msgstr "Keine Beleidigungen oder Hassrede." 607 + 608 + #: letters/templates/letters/letter_form.html:22 609 + msgid "Stay within the bounds of the Grundgesetz when making demands." 610 + msgstr "Bleiben Sie bei Forderungen im Rahmen des Grundgesetzes." 611 + 612 + #: letters/templates/letters/letter_form.html:30 613 msgid "Title:" 614 msgstr "Titel:" 615 616 + #: letters/templates/letters/letter_form.html:36 617 msgid "" 618 + "Describe your concern in a sentence; we'll use it to suggest representatives." 619 msgstr "" 620 + "Beschreiben Sie Ihr Anliegen in einem Satz; wir verwenden ihn, um Ihnen Abgeordnete " 621 + "vorzuschlagen." 622 623 + #: letters/templates/letters/letter_form.html:41 624 msgid "To Representative:" 625 msgstr "An Abgeordnete:" 626 627 + #: letters/templates/letters/letter_form.html:47 628 + msgid "" 629 + "Already know who to address? Pick them here. Otherwise, use the suggestions " 630 + "below." 631 + msgstr "" 632 + "Wissen Sie bereits, wen Sie ansprechen mรถchten? Wรคhlen Sie hier aus. Andernfalls nutzen " 633 + "Sie die Vorschlรคge unten." 634 635 + #: letters/templates/letters/letter_form.html:53 636 msgid "Letter Body:" 637 msgstr "Brieftext:" 638 639 + #: letters/templates/letters/letter_form.html:60 640 msgid "Publish Letter" 641 msgstr "Brief verรถffentlichen" 642 643 + #: letters/templates/letters/letter_form.html:67 644 msgid "Smart Suggestions" 645 msgstr "Intelligente Vorschlรคge" 646 647 + #: letters/templates/letters/letter_form.html:69 648 msgid "" 649 + "Type your title and we'll use your verified profile to suggest " 650 + "representatives, topics, and related letters." 651 msgstr "" 652 + "Geben Sie Ihren Titel ein und wir verwenden Ihr verifiziertes Profil, um Ihnen " 653 + "Abgeordnete, Themen und verwandte Briefe vorzuschlagen." 654 655 + #: letters/templates/letters/letter_form.html:81 656 msgid "Loading..." 657 msgstr "Lรคdt..." 658 659 + #: letters/templates/letters/letter_form.html:83 660 msgid "Analyzing your title..." 661 msgstr "Analysiere Ihren Titel..." 662 ··· 697 msgid "Popular tags:" 698 msgstr "Beliebte Schlagwรถrter:" 699 700 + #: letters/templates/letters/letter_list.html:41 701 + msgid "Previous" 702 + msgstr "Zurรผck" 703 + 704 + #: letters/templates/letters/letter_list.html:43 705 + #, python-format 706 + msgid "Page %(page)s of %(total)s" 707 + msgstr "Seite %(page)s von %(total)s" 708 + 709 + #: letters/templates/letters/letter_list.html:45 710 + msgid "Next" 711 + msgstr "Weiter" 712 + 713 + #: letters/templates/letters/letter_list.html:51 714 + msgid "No letters found." 715 + msgstr "Keine Briefe gefunden." 716 + 717 + #: letters/templates/letters/letter_list.html:51 718 + msgid "Be the first to write one!" 719 + msgstr "Schreiben Sie den ersten!" 720 + 721 # Login page 722 #: letters/templates/letters/login.html:14 723 + #: letters/templates/letters/register.html:15 724 msgid "Username:" 725 msgstr "Benutzername:" 726 727 #: letters/templates/letters/login.html:22 728 + #: letters/templates/letters/register.html:47 729 msgid "Password:" 730 msgstr "Passwort:" 731 732 #: letters/templates/letters/login.html:37 733 + msgid "Forgot your password?" 734 + msgstr "Passwort vergessen?" 735 + 736 + #: letters/templates/letters/login.html:41 737 msgid "Don't have an account?" 738 msgstr "Noch kein Konto?" 739 740 + #: letters/templates/letters/login.html:41 741 msgid "Register here" 742 msgstr "Hier registrieren" 743 744 + #: letters/templates/letters/partials/letter_card.html:6 745 + msgid "To" 746 + msgstr "An" 747 + 748 # Plurals for signatures 749 #: letters/templates/letters/partials/letter_card.html:20 750 #, python-format 751 msgid "%(counter)s signature" 752 msgid_plural "%(counter)s signatures" ··· 754 msgstr[1] "%(counter)s Unterschriften" 755 756 #: letters/templates/letters/partials/letter_card.html:20 757 + #, python-format 758 msgid "%(counter)s verified" 759 msgid_plural "%(counter)s verified" 760 msgstr[0] "%(counter)s verifiziert" 761 msgstr[1] "%(counter)s verifiziert" 762 763 + #: letters/templates/letters/partials/representative_card.html:21 764 + #: letters/templates/letters/partials/representative_card.html:23 765 + msgid "Constituency" 766 + msgstr "Wahlkreis" 767 + 768 + #: letters/templates/letters/partials/representative_card.html:27 769 + msgid "Mandate" 770 + msgstr "Mandat" 771 + 772 + #: letters/templates/letters/partials/representative_card.html:34 773 + msgid "Focus" 774 + msgstr "Schwerpunkt" 775 + 776 + #: letters/templates/letters/partials/representative_card.html:34 777 + msgid "self-declared" 778 + msgstr "selbst erklรคrt" 779 + 780 + #: letters/templates/letters/partials/representative_card.html:45 781 + msgid "Committees" 782 + msgstr "Ausschรผsse" 783 + 784 + #: letters/templates/letters/partials/representative_card.html:57 785 + msgid "Email" 786 + msgstr "E-Mail" 787 + 788 + #: letters/templates/letters/partials/representative_card.html:60 789 + msgid "Website" 790 + msgstr "Webseite" 791 + 792 + #: letters/templates/letters/partials/representative_card.html:66 793 + msgid "View profile" 794 + msgstr "Profil ansehen" 795 + 796 + #: letters/templates/letters/partials/suggestions.html:10 797 + msgid "" 798 + "We couldn't match you to a constituency yet. Update your profile " 799 + "verification to see local representatives." 800 + msgstr "" 801 + "Wir konnten Sie noch keinem Wahlkreis zuordnen. Aktualisieren Sie Ihre Profilverifizierung, " 802 + "um lokale Abgeordnete zu sehen." 803 + 804 # Suggestions partial 805 + #: letters/templates/letters/partials/suggestions.html:16 806 msgid "Our Interpretation" 807 msgstr "Unsere Interpretation" 808 809 + #: letters/templates/letters/partials/suggestions.html:21 810 msgid "Topic:" 811 msgstr "Thema:" 812 813 + #: letters/templates/letters/partials/suggestions.html:28 814 msgid "No specific policy area detected. Try adding more keywords." 815 msgstr "" 816 "Kein spezifischer Politikbereich erkannt. Versuchen Sie, mehr " 817 "Schlรผsselwรถrter hinzuzufรผgen." 818 819 + #: letters/templates/letters/partials/suggestions.html:37 820 + msgid "Related Keywords" 821 + msgstr "Verwandte Schlagwรถrter" 822 + 823 + #: letters/templates/letters/partials/suggestions.html:51 824 + msgid "Your Direct Representatives" 825 + msgstr "Ihre direkten Abgeordneten" 826 + 827 + #: letters/templates/letters/partials/suggestions.html:56 828 + msgid "These representatives directly represent your constituency:" 829 + msgstr "Diese Abgeordneten vertreten Ihren Wahlkreis direkt:" 830 831 + #: letters/templates/letters/partials/suggestions.html:67 832 + #: letters/templates/letters/partials/suggestions.html:102 833 + msgid "Select" 834 + msgstr "Auswรคhlen" 835 + 836 + #: letters/templates/letters/partials/suggestions.html:81 837 + msgid "Topic Experts" 838 + msgstr "Themenexperten" 839 + 840 + #: letters/templates/letters/partials/suggestions.html:86 841 #, python-format 842 msgid "" 843 + "These representatives are experts on \"%(topic)s\" based on their committee " 844 + "memberships:" 845 msgstr "" 846 + "Diese Abgeordneten sind Experten fรผr \"%(topic)s\" basierend auf ihren " 847 + "Ausschussmitgliedschaften:" 848 849 + #: letters/templates/letters/partials/suggestions.html:95 850 msgid "of" 851 msgstr "von" 852 853 + #: letters/templates/letters/partials/suggestions.html:116 854 + msgid "Suggested Representatives" 855 + msgstr "Vorgeschlagene Abgeordnete" 856 857 + #: letters/templates/letters/partials/suggestions.html:119 858 msgid "" 859 "No representatives found. Representatives may need to be synced for this " 860 "governmental level." ··· 862 "Keine Abgeordneten gefunden. Abgeordnete mรผssen mรถglicherweise fรผr diese " 863 "Verwaltungsebene synchronisiert werden." 864 865 + #: letters/templates/letters/partials/suggestions.html:148 866 + msgid "Selected:" 867 + msgstr "Ausgewรคhlt:" 868 + 869 + #: letters/templates/letters/password_reset_complete.html:4 870 + #: letters/templates/letters/password_reset_complete.html:8 871 + msgid "Password updated" 872 + msgstr "Passwort aktualisiert" 873 + 874 + #: letters/templates/letters/password_reset_complete.html:9 875 + msgid "You can now sign in using your new password." 876 + msgstr "Sie kรถnnen sich jetzt mit Ihrem neuen Passwort anmelden." 877 + 878 + #: letters/templates/letters/password_reset_complete.html:10 879 + msgid "Go to login" 880 + msgstr "Zur Anmeldung" 881 + 882 + #: letters/templates/letters/password_reset_confirm.html:4 883 + #: letters/templates/letters/password_reset_confirm.html:9 884 + msgid "Choose a new password" 885 + msgstr "Wรคhlen Sie ein neues Passwort" 886 + 887 + #: letters/templates/letters/password_reset_confirm.html:13 888 + msgid "New password" 889 + msgstr "Neues Passwort" 890 + 891 + #: letters/templates/letters/password_reset_confirm.html:20 892 + msgid "Confirm password" 893 + msgstr "Passwort bestรคtigen" 894 + 895 + #: letters/templates/letters/password_reset_confirm.html:26 896 + msgid "Update password" 897 + msgstr "Passwort aktualisieren" 898 899 + #: letters/templates/letters/password_reset_confirm.html:29 900 + msgid "Reset link invalid" 901 + msgstr "Zurรผcksetzungslink ungรผltig" 902 + 903 + #: letters/templates/letters/password_reset_confirm.html:30 904 + msgid "This password reset link is no longer valid. Please request a new one." 905 + msgstr "Dieser Passwort-Zurรผcksetzungslink ist nicht mehr gรผltig. Bitte fordern Sie einen neuen an." 906 + 907 + #: letters/templates/letters/password_reset_confirm.html:31 908 + msgid "Request new link" 909 + msgstr "Neuen Link anfordern" 910 + 911 + #: letters/templates/letters/password_reset_done.html:4 912 + msgid "Reset email sent" 913 + msgstr "Zurรผcksetzungs-E-Mail gesendet" 914 + 915 + #: letters/templates/letters/password_reset_done.html:9 916 + msgid "" 917 + "If an account exists for that email address, we just sent you instructions " 918 + "to choose a new password." 919 msgstr "" 920 + "Falls ein Konto fรผr diese E-Mail-Adresse existiert, haben wir Ihnen gerade Anweisungen " 921 + "zum Festlegen eines neuen Passworts gesendet." 922 923 + #: letters/templates/letters/password_reset_done.html:10 924 + msgid "The link will stay valid for a limited time." 925 + msgstr "Der Link bleibt fรผr eine begrenzte Zeit gรผltig." 926 927 + #: letters/templates/letters/password_reset_done.html:11 928 + msgid "Back to login" 929 + msgstr "Zurรผck zur Anmeldung" 930 931 + #: letters/templates/letters/password_reset_form.html:4 932 + msgid "Reset password" 933 + msgstr "Passwort zurรผcksetzen" 934 935 + #: letters/templates/letters/password_reset_form.html:8 936 + msgid "Reset your password" 937 + msgstr "Setzen Sie Ihr Passwort zurรผck" 938 939 + #: letters/templates/letters/password_reset_form.html:9 940 + msgid "" 941 + "Enter the email address you used during registration. We will send you a " 942 + "link to create a new password." 943 msgstr "" 944 + "Geben Sie die E-Mail-Adresse ein, die Sie bei der Registrierung verwendet haben. Wir " 945 + "senden Ihnen einen Link zum Erstellen eines neuen Passworts." 946 947 + #: letters/templates/letters/password_reset_form.html:17 948 + msgid "Send reset link" 949 + msgstr "Zurรผcksetzungslink senden" 950 951 # Profile page 952 + #: letters/templates/letters/profile.html:13 953 #, python-format 954 msgid "%(username)s's Profile" 955 msgstr "Profil von %(username)s" 956 957 + #: letters/templates/letters/profile.html:16 958 + msgid "Identity & Constituency" 959 + msgstr "Identitรคt & Wahlkreis" 960 961 + #: letters/templates/letters/profile.html:19 962 + msgid "Status:" 963 + msgstr "Status:" 964 965 #: letters/templates/letters/profile.html:21 966 + msgid "Type:" 967 + msgstr "Typ:" 968 + 969 + #: letters/templates/letters/profile.html:28 970 + msgid "" 971 + "You self-declared your constituency. Representatives will see your " 972 + "signatures as self-declared constituents." 973 msgstr "" 974 + "Sie haben Ihren Wahlkreis selbst angegeben. Abgeordnete werden Ihre Unterschriften als " 975 + "selbst angegebene Wรคhler sehen." 976 977 + #: letters/templates/letters/profile.html:30 978 + msgid "Start third-party verification" 979 + msgstr "Drittanbieter-Verifizierung starten" 980 + 981 + #: letters/templates/letters/profile.html:33 982 + msgid "" 983 + "Your identity was verified via a third-party provider. Signatures will " 984 + "appear as verified constituents." 985 + msgstr "" 986 + "Ihre Identitรคt wurde รผber einen Drittanbieter verifiziert. Unterschriften werden als " 987 + "verifizierte Wรคhler angezeigt." 988 989 + #: letters/templates/letters/profile.html:38 990 msgid "Your verification is being processed." 991 msgstr "Ihre Verifizierung wird bearbeitet." 992 993 + #: letters/templates/letters/profile.html:39 994 msgid "Complete Verification (Stub)" 995 msgstr "Verifizierung abschlieรŸen (Stub)" 996 997 + #: letters/templates/letters/profile.html:43 998 + msgid "Verification failed. Please try again or contact support." 999 + msgstr "Verifizierung fehlgeschlagen. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support." 1000 1001 + #: letters/templates/letters/profile.html:48 1002 + msgid "" 1003 + "You can self-declare your constituency below or start a verification with a " 1004 + "trusted provider. Verified signatures carry more weight." 1005 + msgstr "" 1006 + "Sie kรถnnen Ihren Wahlkreis unten selbst angeben oder eine Verifizierung mit einem " 1007 + "vertrauenswรผrdigen Anbieter starten. Verifizierte Unterschriften haben mehr Gewicht." 1008 1009 + #: letters/templates/letters/profile.html:50 1010 + msgid "Start Third-party Verification" 1011 + msgstr "Drittanbieter-Verifizierung starten" 1012 + 1013 + #: letters/templates/letters/profile.html:55 1014 + msgid "Ihre Adresse" 1015 + msgstr "Ihre Adresse" 1016 + 1017 + #: letters/templates/letters/profile.html:57 1018 msgid "" 1019 + "Geben Sie Ihre vollstรคndige Adresse ein, um prรคzise Wahlkreis- und " 1020 + "Abgeordnetenempfehlungen zu erhalten." 1021 msgstr "" 1022 + "Geben Sie Ihre vollstรคndige Adresse ein, um prรคzise Wahlkreis- und " 1023 + "Abgeordnetenempfehlungen zu erhalten." 1024 1025 + #: letters/templates/letters/profile.html:61 1026 + msgid "Gespeicherte Adresse:" 1027 + msgstr "Gespeicherte Adresse:" 1028 + 1029 + #: letters/templates/letters/profile.html:83 1030 + msgid "Adresse speichern" 1031 + msgstr "Adresse speichern" 1032 1033 + #: letters/templates/letters/profile.html:88 1034 + msgid "Self-declare your constituency" 1035 + msgstr "Geben Sie Ihren Wahlkreis selbst an" 1036 + 1037 + #: letters/templates/letters/profile.html:90 1038 + msgid "" 1039 + "Select the constituencies you live in so we can prioritise the right " 1040 + "representatives." 1041 + msgstr "" 1042 + "Wรคhlen Sie die Wahlkreise aus, in denen Sie leben, damit wir die richtigen " 1043 + "Abgeordneten priorisieren kรถnnen." 1044 + 1045 + #: letters/templates/letters/profile.html:109 1046 + msgid "Save constituencies" 1047 + msgstr "Wahlkreise speichern" 1048 + 1049 + #: letters/templates/letters/profile.html:114 1050 msgid "Your Letters" 1051 msgstr "Ihre Briefe" 1052 1053 + #: letters/templates/letters/profile.html:120 1054 msgid "You haven't written any letters yet." 1055 msgstr "Sie haben noch keine Briefe geschrieben." 1056 1057 + #: letters/templates/letters/profile.html:120 1058 msgid "Write one now!" 1059 msgstr "Schreiben Sie jetzt einen!" 1060 1061 + #: letters/templates/letters/profile.html:125 1062 msgid "Letters You've Signed" 1063 msgstr "Briefe, die Sie unterzeichnet haben" 1064 1065 + #: letters/templates/letters/profile.html:133 1066 msgid "You haven't signed any letters yet." 1067 msgstr "Sie haben noch keine Briefe unterzeichnet." 1068 1069 + #: letters/templates/letters/profile.html:133 1070 msgid "Browse letters" 1071 msgstr "Briefe durchsuchen" 1072 1073 + #: letters/templates/letters/profile.html:138 1074 + msgid "Account" 1075 + msgstr "Konto" 1076 + 1077 + #: letters/templates/letters/profile.html:140 1078 + msgid "" 1079 + "Need a fresh start? You can delete your account at any time. Your letters " 1080 + "stay visible but without your name." 1081 + msgstr "" 1082 + "Brauchen Sie einen Neuanfang? Sie kรถnnen Ihr Konto jederzeit lรถschen. Ihre Briefe " 1083 + "bleiben sichtbar, aber ohne Ihren Namen." 1084 + 1085 + #: letters/templates/letters/register.html:9 1086 + msgid "" 1087 + "After registration we'll send you an email to confirm your address before " 1088 + "you can sign in." 1089 + msgstr "" 1090 + "Nach der Registrierung senden wir Ihnen eine E-Mail, um Ihre Adresse zu bestรคtigen, " 1091 + "bevor Sie sich anmelden kรถnnen." 1092 + 1093 + #: letters/templates/letters/register.html:23 1094 msgid "Email:" 1095 msgstr "E-Mail:" 1096 1097 # Register page 1098 + #: letters/templates/letters/register.html:31 1099 msgid "First Name (optional):" 1100 msgstr "Vorname (optional):" 1101 1102 + #: letters/templates/letters/register.html:39 1103 msgid "Last Name (optional):" 1104 msgstr "Nachname (optional):" 1105 1106 + #: letters/templates/letters/register.html:55 1107 msgid "Confirm Password:" 1108 msgstr "Passwort bestรคtigen:" 1109 1110 + #: letters/templates/letters/register.html:70 1111 msgid "Already have an account?" 1112 msgstr "Bereits ein Konto?" 1113 1114 + #: letters/templates/letters/register.html:70 1115 msgid "Login here" 1116 msgstr "Hier anmelden" 1117 1118 #: letters/templates/letters/representative_detail.html:19 1119 + msgid "รœber" 1120 + msgstr "รœber" 1121 1122 + #: letters/templates/letters/representative_detail.html:30 1123 msgid "Committee Memberships" 1124 msgstr "Ausschussmitgliedschaften" 1125 1126 + #: letters/templates/letters/representative_detail.html:75 1127 msgid "Open Letters" 1128 msgstr "Offene Briefe" 1129 1130 + #: letters/templates/letters/representative_detail.html:83 1131 msgid "No letters have been written to this representative yet." 1132 msgstr "An diesen Abgeordneten wurden noch keine Briefe geschrieben." 1133 1134 + #: letters/templates/letters/representative_detail.html:85 1135 msgid "Write the First Letter" 1136 msgstr "Ersten Brief schreiben" 1137 1138 + #: letters/templates/letters/representative_detail.html:95 1139 msgid "External Resources" 1140 msgstr "Externe Ressourcen" 1141 1142 + #: letters/templates/letters/representative_detail.html:100 1143 msgid "Abgeordnetenwatch Profile" 1144 msgstr "Abgeordnetenwatch-Profil" 1145 1146 + #: letters/templates/letters/representative_detail.html:102 1147 msgid "" 1148 "View voting record, questions, and detailed profile on Abgeordnetenwatch.de" 1149 msgstr "" 1150 "Abstimmungsverhalten, Fragen und detailliertes Profil auf Abgeordnetenwatch." 1151 "de ansehen" 1152 1153 + #: letters/templates/letters/representative_detail.html:112 1154 + msgid "Wikipedia Article" 1155 + msgstr "Wikipedia-Artikel" 1156 1157 + #: letters/templates/letters/representative_detail.html:114 1158 msgid "Read more about this representative on Wikipedia" 1159 msgstr "Mehr รผber diesen Abgeordneten auf Wikipedia lesen" 1160 1161 + #: letters/templates/letters/representative_detail.html:117 1162 msgid "View on Wikipedia" 1163 msgstr "Auf Wikipedia ansehen" 1164 1165 + #: letters/templates/letters/representative_detail.html:123 1166 msgid "No external resources available for this representative." 1167 msgstr "Keine externen Ressourcen fรผr diesen Abgeordneten verfรผgbar." 1168 1169 + #: letters/templates/letters/representative_detail.html:130 1170 + msgid "Kontakt" 1171 + msgstr "Kontakt" 1172 + 1173 + #: letters/templates/letters/representative_detail.html:145 1174 #, python-format 1175 msgid "Start a new open letter to %(name)s" 1176 msgstr "Einen neuen offenen Brief an %(name)s beginnen" 1177 1178 + #: letters/templates/letters/representative_detail.html:153 1179 msgid "Login to Write Letter" 1180 msgstr "Anmelden um Brief zu schreiben" 1181 1182 + #: letters/views.py:52 1183 + msgid "Confirm your WriteThem.eu account" 1184 + msgstr "Bestรคtigen Sie Ihr WriteThem.eu-Konto" 1185 + 1186 # Flash messages 1187 + #: letters/views.py:184 1188 msgid "Your letter has been published and your signature has been added!" 1189 + msgstr "Ihr Brief wurde verรถffentlicht und Ihre Unterschrift wurde hinzugefรผgt!" 1190 1191 + #: letters/views.py:197 1192 msgid "You have already signed this letter." 1193 msgstr "Sie haben diesen Brief bereits unterzeichnet." 1194 1195 + #: letters/views.py:207 1196 msgid "Your signature has been added!" 1197 msgstr "Ihre Unterschrift wurde hinzugefรผgt!" 1198 1199 + #: letters/views.py:225 1200 msgid "Thank you for your report. Our team will review it." 1201 msgstr "Vielen Dank fรผr Ihre Meldung. Unser Team wird sie รผberprรผfen." 1202 1203 + #: letters/views.py:259 1204 + msgid "" 1205 + "Please confirm your email address. We sent you a link to activate your " 1206 + "account." 1207 + msgstr "" 1208 + "Bitte bestรคtigen Sie Ihre E-Mail-Adresse. Wir haben Ihnen einen Link zum Aktivieren " 1209 + "Ihres Kontos gesendet." 1210 1211 + #: letters/views.py:289 1212 + msgid "Your account has been activated. You can now log in." 1213 + msgstr "Ihr Konto wurde aktiviert. Sie kรถnnen sich jetzt anmelden." 1214 + 1215 + #: letters/views.py:292 1216 + msgid "Your account is already active." 1217 + msgstr "Ihr Konto ist bereits aktiv." 1218 1219 + #: letters/views.py:347 1220 + msgid "Ihre Adresse wurde gespeichert." 1221 + msgstr "Ihre Adresse wurde gespeichert." 1222 + 1223 + #: letters/views.py:363 1224 + msgid "Your constituency information has been updated." 1225 + msgstr "Ihre Wahlkreisinformationen wurden aktualisiert." 1226 + 1227 + #: letters/views.py:391 1228 + msgid "" 1229 + "Your account has been deleted. Your published letters remain available to " 1230 + "the public." 1231 + msgstr "" 1232 + "Ihr Konto wurde gelรถscht. Ihre verรถffentlichten Briefe bleiben fรผr die " 1233 + "ร–ffentlichkeit verfรผgbar." 1234 + 1235 + # Forms 1236 + #~ msgid "Postal code (PLZ)" 1237 + #~ msgstr "Postleitzahl (PLZ)" 1238 + 1239 + #, fuzzy 1240 + #~| msgid "Use your PLZ to narrow down representatives from your constituency." 1241 + #~ msgid "Use your PLZ to narrow down representatives from your parliament." 1242 + #~ msgstr "" 1243 + #~ "Verwenden Sie Ihre PLZ, um Abgeordnete aus Ihrem Wahlkreis einzugrenzen." 1244 + 1245 + #~ msgid "Comma-separated tags (e.g., \"climate, transport, education\")" 1246 + #~ msgstr "Komma-getrennte Schlagwรถrter (z.B. \"Klima, Verkehr, Bildung\")" 1247 + 1248 + #~ msgid "climate, transport, education" 1249 + #~ msgstr "Klima, Verkehr, Bildung" 1250 + 1251 + #~ msgid "Write your letter here" 1252 + #~ msgstr "Schreiben Sie hier Ihren Brief" 1253 + 1254 + #, fuzzy 1255 + #~| msgid "Federal" 1256 + #~ msgid "Federal district" 1257 + #~ msgstr "Bund" 1258 + 1259 + #~ msgid "Your postal code (PLZ):" 1260 + #~ msgstr "Ihre Postleitzahl (PLZ):" 1261 + 1262 + #~ msgid "Or select from suggestions on the right โ†’" 1263 + #~ msgstr "Oder wรคhlen Sie aus den Vorschlรคgen rechts โ†’" 1264 + 1265 + #~ msgid "Tags (optional):" 1266 + #~ msgstr "Schlagwรถrter (optional):" 1267 1268 #, python-format 1269 + #~ msgid "" 1270 + #~ "Based on the topic \"%(topic)s\", we suggest contacting representatives " 1271 + #~ "from the %(level)s:" 1272 + #~ msgstr "" 1273 + #~ "Basierend auf dem Thema \"%(topic)s\" empfehlen wir, Abgeordnete des " 1274 + #~ "%(level)s zu kontaktieren:" 1275 + 1276 + #, fuzzy 1277 + #~| msgid "Suggested Representatives" 1278 + #~ msgid "Suggested Tags" 1279 + #~ msgstr "Vorgeschlagene Abgeordnete" 1280 + 1281 + #~ msgid "Similar Letters" 1282 + #~ msgstr "ร„hnliche Briefe" 1283 + 1284 + #~ msgid "Others have written about similar topics:" 1285 + #~ msgstr "Andere haben รผber รคhnliche Themen geschrieben:" 1286 1287 + #, fuzzy 1288 + #~| msgid "To" 1289 + #~ msgid "To:" 1290 + #~ msgstr "An" 1291 + 1292 + #~ msgid "Identity Verification" 1293 + #~ msgstr "Identitรคtsverifizierung" 1294 1295 + #~ msgid "Your signatures will be marked as verified constituent signatures." 1296 + #~ msgstr "" 1297 + #~ "Ihre Unterschriften werden als verifizierte Wรคhlerunterschriften markiert." 1298 + 1299 + #~ msgid "Verification Pending" 1300 + #~ msgstr "Verifizierung ausstehend" 1301 + 1302 + #~ msgid "Verification Failed" 1303 + #~ msgstr "Verifizierung fehlgeschlagen" 1304 + 1305 + # Letter detail page 1306 + #~ msgid "Signed on" 1307 + #~ msgstr "Unterzeichnet am" 1308 + 1309 + #~ msgid "Your comment:" 1310 + #~ msgstr "Ihr Kommentar:" 1311 + 1312 + # Representative detail 1313 + #~ msgid "Party:" 1314 + #~ msgstr "Partei:" 1315 + 1316 + #~ msgid "Legislative Body:" 1317 + #~ msgstr "Parlament:" 1318 + 1319 + #~ msgid "Present" 1320 + #~ msgstr "Heute" 1321 + 1322 + #~ msgid "Inactive" 1323 + #~ msgstr "Inaktiv" 1324 + 1325 + #~ msgid "" 1326 + #~ "Based on committee memberships, this representative works on the " 1327 + #~ "following policy areas:" 1328 + #~ msgstr "" 1329 + #~ "Basierend auf Ausschussmitgliedschaften arbeitet dieser Abgeordnete in " 1330 + #~ "folgenden Politikbereichen:" 1331 + 1332 + #, python-format 1333 + #~ msgid "Welcome, %(username)s! Your account has been created." 1334 + #~ msgstr "Willkommen, %(username)s! Ihr Konto wurde erstellt." 1335 1336 #, python-format 1337 #~ msgid "<strong>%(counter)s</strong> other verified" ··· 1349 1350 #~ msgid "Committee Memberships:" 1351 #~ msgstr "Ausschussmitgliedschaften:" 1352 1353 # Services explanations 1354 #~ msgid "No matching policy areas found. Please try different keywords."
website/locale/en/LC_MESSAGES/.gitkeep

This is a binary file and will not be displayed.

+1218
website/locale/en/LC_MESSAGES/django.po
···
··· 1 + # SOME DESCRIPTIVE TITLE. 2 + # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 + # This file is distributed under the same license as the PACKAGE package. 4 + # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. 5 + # 6 + #, fuzzy 7 + msgid "" 8 + msgstr "" 9 + "Project-Id-Version: PACKAGE VERSION\n" 10 + "Report-Msgid-Bugs-To: \n" 11 + "POT-Creation-Date: 2025-10-15 00:28+0200\n" 12 + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 + "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 14 + "Language-Team: LANGUAGE <LL@li.org>\n" 15 + "Language: \n" 16 + "MIME-Version: 1.0\n" 17 + "Content-Type: text/plain; charset=UTF-8\n" 18 + "Content-Transfer-Encoding: 8bit\n" 19 + "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 + 21 + #: letters/admin.py:75 22 + msgid "Mandate Details" 23 + msgstr "Mandate Details" 24 + 25 + #: letters/admin.py:78 26 + msgid "Focus Areas" 27 + msgstr "Focus Areas" 28 + 29 + #: letters/admin.py:81 letters/admin.py:95 30 + msgid "Photo" 31 + msgstr "Photo" 32 + 33 + #: letters/admin.py:85 34 + msgid "Metadata" 35 + msgstr "Metadata" 36 + 37 + #: letters/admin.py:94 38 + msgid "No photo" 39 + msgstr "No photo" 40 + 41 + #: letters/admin.py:167 42 + msgid "Topic Areas" 43 + msgstr "Topic Areas" 44 + 45 + #: letters/forms.py:25 46 + msgid "" 47 + "An account with this email already exists. If you registered before, please " 48 + "check your inbox for the activation link or reset your password." 49 + msgstr "" 50 + "An account with this email already exists. If you registered before, please " 51 + "check your inbox for the activation link or reset your password." 52 + 53 + #: letters/forms.py:37 54 + msgid "Title" 55 + msgstr "Title" 56 + 57 + #: letters/forms.py:38 58 + msgid "Letter Body" 59 + msgstr "Letter Body" 60 + 61 + #: letters/forms.py:39 62 + msgid "To Representative" 63 + msgstr "To Representative" 64 + 65 + #: letters/forms.py:42 66 + msgid "Describe your concern briefly" 67 + msgstr "Describe your concern briefly" 68 + 69 + #: letters/forms.py:43 70 + msgid "" 71 + "Write your letter here. Markdown formatting (e.g. **bold**, _italic_) is " 72 + "supported." 73 + msgstr "" 74 + "Write your letter here. Markdown formatting (e.g. **bold**, _italic_) is " 75 + "supported." 76 + 77 + #: letters/forms.py:46 78 + msgid "Letter title" 79 + msgstr "Letter title" 80 + 81 + #: letters/forms.py:50 82 + msgid "Write your letter here..." 83 + msgstr "Write your letter here..." 84 + 85 + #: letters/forms.py:110 86 + msgid "Comment (optional)" 87 + msgstr "Comment (optional)" 88 + 89 + #: letters/forms.py:113 90 + msgid "Add a personal note to your signature" 91 + msgstr "Add a personal note to your signature" 92 + 93 + #: letters/forms.py:119 94 + msgid "Optional: Add your comment..." 95 + msgstr "Optional: Add your comment..." 96 + 97 + #: letters/forms.py:131 98 + msgid "Reason" 99 + msgstr "Reason" 100 + 101 + #: letters/forms.py:132 102 + msgid "Description" 103 + msgstr "Description" 104 + 105 + #: letters/forms.py:135 106 + msgid "Please provide details about why you are reporting this letter" 107 + msgstr "Please provide details about why you are reporting this letter" 108 + 109 + #: letters/forms.py:142 110 + msgid "Please describe the issue..." 111 + msgstr "Please describe the issue..." 112 + 113 + #: letters/forms.py:152 letters/templates/letters/letter_list.html:19 114 + msgid "Search" 115 + msgstr "Search" 116 + 117 + #: letters/forms.py:155 letters/templates/letters/letter_list.html:18 118 + msgid "Search letters..." 119 + msgstr "Search letters..." 120 + 121 + #: letters/forms.py:161 122 + msgid "Tag" 123 + msgstr "Tag" 124 + 125 + #: letters/forms.py:164 126 + msgid "Filter by tag..." 127 + msgstr "Filter by tag..." 128 + 129 + #: letters/forms.py:180 130 + msgid "Bundestag constituency" 131 + msgstr "Bundestag constituency" 132 + 133 + #: letters/forms.py:181 134 + msgid "Pick your Bundestag direct mandate constituency (Wahlkreis)." 135 + msgstr "Pick your Bundestag direct mandate constituency (Wahlkreis)." 136 + 137 + #: letters/forms.py:182 letters/forms.py:189 138 + msgid "Select constituency" 139 + msgstr "Select constituency" 140 + 141 + #: letters/forms.py:187 142 + msgid "State parliament constituency" 143 + msgstr "State parliament constituency" 144 + 145 + #: letters/forms.py:188 146 + msgid "Optionally pick your Landtag constituency if applicable." 147 + msgstr "Optionally pick your Landtag constituency if applicable." 148 + 149 + #: letters/forms.py:240 150 + msgid "Please select at least one constituency to save your profile." 151 + msgstr "Please select at least one constituency to save your profile." 152 + 153 + #: letters/forms.py:252 154 + msgid "StraรŸe und Hausnummer" 155 + msgstr "StraรŸe und Hausnummer" 156 + 157 + #: letters/forms.py:255 158 + msgid "z.B. Unter den Linden 77" 159 + msgstr "z.B. Unter den Linden 77" 160 + 161 + #: letters/forms.py:261 162 + msgid "Postleitzahl" 163 + msgstr "Postleitzahl" 164 + 165 + #: letters/forms.py:264 166 + msgid "z.B. 10117" 167 + msgstr "z.B. 10117" 168 + 169 + #: letters/forms.py:270 170 + msgid "Stadt" 171 + msgstr "Stadt" 172 + 173 + #: letters/forms.py:273 174 + msgid "z.B. Berlin" 175 + msgstr "z.B. Berlin" 176 + 177 + #: letters/forms.py:304 178 + msgid "" 179 + "Bitte geben Sie eine vollstรคndige Adresse ein (StraรŸe, PLZ und Stadt) oder " 180 + "lassen Sie alle Felder leer." 181 + msgstr "" 182 + "Bitte geben Sie eine vollstรคndige Adresse ein (StraรŸe, PLZ und Stadt) oder " 183 + "lassen Sie alle Felder leer." 184 + 185 + #: letters/models.py:15 186 + msgid "European Union" 187 + msgstr "European Union" 188 + 189 + #: letters/models.py:16 190 + msgid "Federal" 191 + msgstr "Federal" 192 + 193 + #: letters/models.py:17 194 + msgid "State" 195 + msgstr "State" 196 + 197 + #: letters/models.py:18 198 + msgid "Local" 199 + msgstr "Local" 200 + 201 + #: letters/models.py:21 202 + msgid "Name of the parliament" 203 + msgstr "Name of the parliament" 204 + 205 + #: letters/models.py:25 206 + msgid "e.g., 'Bundestag', 'Bayerischer Landtag', 'Gemeinderat Mรผnchen'" 207 + msgstr "e.g., 'Bundestag', 'Bayerischer Landtag', 'Gemeinderat Mรผnchen'" 208 + 209 + #: letters/models.py:29 210 + msgid "Geographic identifier (state code, municipality code, etc.)" 211 + msgstr "Geographic identifier (state code, municipality code, etc.)" 212 + 213 + #: letters/models.py:37 214 + msgid "For hierarchical relationships (e.g., local within state)" 215 + msgstr "For hierarchical relationships (e.g., local within state)" 216 + 217 + #: letters/models.py:43 letters/models.py:70 letters/models.py:105 218 + #: letters/models.py:165 letters/models.py:372 letters/models.py:421 219 + msgid "Last time this was synced from external API" 220 + msgstr "Last time this was synced from external API" 221 + 222 + #: letters/models.py:47 letters/templates/letters/committee_detail.html:70 223 + msgid "Parliament" 224 + msgstr "Parliament" 225 + 226 + #: letters/models.py:48 227 + msgid "Parliaments" 228 + msgstr "Parliaments" 229 + 230 + #: letters/models.py:84 231 + msgid "Federal electoral district" 232 + msgstr "Federal electoral district" 233 + 234 + #: letters/models.py:85 235 + msgid "Bundestag state list" 236 + msgstr "Bundestag state list" 237 + 238 + #: letters/models.py:86 239 + msgid "Bundestag federal list" 240 + msgstr "Bundestag federal list" 241 + 242 + #: letters/models.py:87 243 + msgid "State electoral district" 244 + msgstr "State electoral district" 245 + 246 + #: letters/models.py:88 247 + msgid "State regional list" 248 + msgstr "State regional list" 249 + 250 + #: letters/models.py:89 251 + msgid "State wide list" 252 + msgstr "State wide list" 253 + 254 + #: letters/models.py:90 255 + msgid "EU at large" 256 + msgstr "EU at large" 257 + 258 + #: letters/models.py:119 259 + msgid "Direct mandate" 260 + msgstr "Direct mandate" 261 + 262 + #: letters/models.py:120 263 + msgid "State list mandate" 264 + msgstr "State list mandate" 265 + 266 + #: letters/models.py:121 267 + msgid "State regional list mandate" 268 + msgstr "State regional list mandate" 269 + 270 + #: letters/models.py:122 271 + msgid "Federal list mandate" 272 + msgstr "Federal list mandate" 273 + 274 + #: letters/models.py:123 275 + msgid "EU list mandate" 276 + msgstr "EU list mandate" 277 + 278 + #: letters/models.py:444 279 + msgid "Draft" 280 + msgstr "Draft" 281 + 282 + #: letters/models.py:445 283 + msgid "Published" 284 + msgstr "Published" 285 + 286 + #: letters/models.py:446 287 + msgid "Flagged for Review" 288 + msgstr "Flagged for Review" 289 + 290 + #: letters/models.py:447 291 + msgid "Removed" 292 + msgstr "Removed" 293 + 294 + #: letters/models.py:487 295 + msgid "Deleted user" 296 + msgstr "Deleted user" 297 + 298 + #: letters/services.py:2451 299 + #, python-format 300 + msgid "Detected policy area: %(topic)s." 301 + msgstr "Detected policy area: %(topic)s." 302 + 303 + #: letters/services.py:2456 304 + #, python-format 305 + msgid "Prioritising representatives for %(constituencies)s." 306 + msgstr "Prioritising representatives for %(constituencies)s." 307 + 308 + #: letters/services.py:2461 309 + #, python-format 310 + msgid "Filtering by state %(state)s." 311 + msgstr "Filtering by state %(state)s." 312 + 313 + #: letters/services.py:2465 314 + #, python-format 315 + msgid "" 316 + "Postal code %(plz)s had no direct match; showing broader representatives." 317 + msgstr "" 318 + "Postal code %(plz)s had no direct match; showing broader representatives." 319 + 320 + #: letters/services.py:2469 321 + msgid "Showing generally relevant representatives." 322 + msgstr "Showing generally relevant representatives." 323 + 324 + #: letters/templates/letters/account_activation_invalid.html:4 325 + #: letters/templates/letters/account_activation_invalid.html:8 326 + msgid "Activation link invalid" 327 + msgstr "Activation link invalid" 328 + 329 + #: letters/templates/letters/account_activation_invalid.html:10 330 + msgid "" 331 + "We could not verify your activation link. It may have already been used or " 332 + "expired." 333 + msgstr "" 334 + "We could not verify your activation link. It may have already been used or " 335 + "expired." 336 + 337 + #: letters/templates/letters/account_activation_invalid.html:13 338 + msgid "" 339 + "If you still cannot access your account, try registering again or contact " 340 + "support." 341 + msgstr "" 342 + "If you still cannot access your account, try registering again or contact " 343 + "support." 344 + 345 + #: letters/templates/letters/account_activation_invalid.html:15 346 + msgid "Register again" 347 + msgstr "Register again" 348 + 349 + #: letters/templates/letters/account_activation_sent.html:4 350 + msgid "Activate your account" 351 + msgstr "Activate your account" 352 + 353 + #: letters/templates/letters/account_activation_sent.html:8 354 + #: letters/templates/letters/password_reset_done.html:8 355 + msgid "Check your inbox" 356 + msgstr "Check your inbox" 357 + 358 + #: letters/templates/letters/account_activation_sent.html:10 359 + msgid "" 360 + "We sent you an email with a confirmation link. Please click it to activate " 361 + "your account." 362 + msgstr "" 363 + "We sent you an email with a confirmation link. Please click it to activate " 364 + "your account." 365 + 366 + #: letters/templates/letters/account_activation_sent.html:13 367 + msgid "" 368 + "If you do not receive the email within a few minutes, check your spam folder " 369 + "or try registering again." 370 + msgstr "" 371 + "If you do not receive the email within a few minutes, check your spam folder " 372 + "or try registering again." 373 + 374 + #: letters/templates/letters/account_activation_sent.html:15 375 + msgid "Back to homepage" 376 + msgstr "Back to homepage" 377 + 378 + #: letters/templates/letters/account_delete_confirm.html:4 379 + #: letters/templates/letters/profile.html:142 380 + msgid "Delete account" 381 + msgstr "Delete account" 382 + 383 + #: letters/templates/letters/account_delete_confirm.html:8 384 + msgid "Delete your account" 385 + msgstr "Delete your account" 386 + 387 + #: letters/templates/letters/account_delete_confirm.html:10 388 + msgid "" 389 + "Deleting your account will remove your personal data and signatures. Letters " 390 + "you have published stay online but are shown without your name." 391 + msgstr "" 392 + "Deleting your account will remove your personal data and signatures. Letters " 393 + "you have published stay online but are shown without your name." 394 + 395 + #: letters/templates/letters/account_delete_confirm.html:14 396 + msgid "Yes, delete my account" 397 + msgstr "Yes, delete my account" 398 + 399 + #: letters/templates/letters/account_delete_confirm.html:15 400 + #: letters/templates/letters/letter_form.html:61 401 + msgid "Cancel" 402 + msgstr "Cancel" 403 + 404 + #: letters/templates/letters/base.html:140 405 + msgid "Letters" 406 + msgstr "Letters" 407 + 408 + #: letters/templates/letters/base.html:141 409 + msgid "Competencies" 410 + msgstr "Competencies" 411 + 412 + #: letters/templates/letters/base.html:143 413 + #: letters/templates/letters/representative_detail.html:149 414 + msgid "Write Letter" 415 + msgstr "Write Letter" 416 + 417 + #: letters/templates/letters/base.html:144 418 + #: letters/templates/letters/profile.html:4 419 + msgid "Profile" 420 + msgstr "Profile" 421 + 422 + #: letters/templates/letters/base.html:145 423 + msgid "Logout" 424 + msgstr "Logout" 425 + 426 + #: letters/templates/letters/base.html:147 427 + #: letters/templates/letters/letter_detail.html:47 428 + #: letters/templates/letters/letter_detail.html:81 429 + #: letters/templates/letters/login.html:4 430 + #: letters/templates/letters/login.html:8 431 + #: letters/templates/letters/login.html:33 432 + msgid "Login" 433 + msgstr "Login" 434 + 435 + #: letters/templates/letters/base.html:148 436 + #: letters/templates/letters/register.html:4 437 + #: letters/templates/letters/register.html:8 438 + #: letters/templates/letters/register.html:66 439 + msgid "Register" 440 + msgstr "Register" 441 + 442 + #: letters/templates/letters/base.html:151 443 + msgid "Admin" 444 + msgstr "Admin" 445 + 446 + #: letters/templates/letters/base.html:157 447 + msgid "Select language" 448 + msgstr "Select language" 449 + 450 + #: letters/templates/letters/base.html:182 451 + msgid "Empowering citizens to write to their representatives" 452 + msgstr "Empowering citizens to write to their representatives" 453 + 454 + #: letters/templates/letters/committee_detail.html:22 455 + msgid "Related Topics" 456 + msgstr "Related Topics" 457 + 458 + #: letters/templates/letters/committee_detail.html:36 459 + msgid "Members" 460 + msgstr "Members" 461 + 462 + #: letters/templates/letters/committee_detail.html:46 463 + msgid "Role" 464 + msgstr "Role" 465 + 466 + #: letters/templates/letters/committee_detail.html:48 467 + #: letters/templates/letters/representative_detail.html:61 468 + msgid "Active" 469 + msgstr "Active" 470 + 471 + #: letters/templates/letters/committee_detail.html:51 472 + #: letters/templates/letters/partials/representative_card.html:29 473 + msgid "Since" 474 + msgstr "Since" 475 + 476 + #: letters/templates/letters/committee_detail.html:58 477 + msgid "No members recorded for this committee." 478 + msgstr "No members recorded for this committee." 479 + 480 + #: letters/templates/letters/committee_detail.html:67 481 + msgid "Committee Info" 482 + msgstr "Committee Info" 483 + 484 + #: letters/templates/letters/committee_detail.html:71 485 + msgid "Term" 486 + msgstr "Term" 487 + 488 + #: letters/templates/letters/committee_detail.html:74 489 + #: letters/templates/letters/representative_detail.html:105 490 + msgid "View on Abgeordnetenwatch" 491 + msgstr "View on Abgeordnetenwatch" 492 + 493 + #: letters/templates/letters/letter_detail.html:10 494 + #: letters/templates/letters/partials/letter_card.html:5 495 + msgid "By" 496 + msgstr "By" 497 + 498 + #: letters/templates/letters/letter_detail.html:29 499 + #, python-format 500 + msgid "Signatures (%(counter)s)" 501 + msgid_plural "Signatures (%(counter)s)" 502 + msgstr[0] "Signatures (%(counter)s)" 503 + msgstr[1] "Signatures (%(counter)s)" 504 + 505 + #: letters/templates/letters/letter_detail.html:31 506 + #, python-format 507 + msgid "%(counter)s constituent of %(constituency_name)s" 508 + msgid_plural "%(counter)s constituents of %(constituency_name)s" 509 + msgstr[0] "%(counter)s constituent of %(constituency_name)s" 510 + msgstr[1] "%(counter)s constituents of %(constituency_name)s" 511 + 512 + #: letters/templates/letters/letter_detail.html:32 513 + #, python-format 514 + msgid "%(counter)s other verified" 515 + msgid_plural "%(counter)s other verified" 516 + msgstr[0] "%(counter)s other verified" 517 + msgstr[1] "%(counter)s other verified" 518 + 519 + #: letters/templates/letters/letter_detail.html:33 520 + #, python-format 521 + msgid "%(counter)s unverified" 522 + msgid_plural "%(counter)s unverified" 523 + msgstr[0] "%(counter)s unverified" 524 + msgstr[1] "%(counter)s unverified" 525 + 526 + #: letters/templates/letters/letter_detail.html:41 527 + msgid "Sign this letter" 528 + msgstr "Sign this letter" 529 + 530 + #: letters/templates/letters/letter_detail.html:44 531 + msgid "You have signed this letter" 532 + msgstr "You have signed this letter" 533 + 534 + #: letters/templates/letters/letter_detail.html:47 535 + msgid "to sign this letter" 536 + msgstr "to sign this letter" 537 + 538 + #: letters/templates/letters/letter_detail.html:58 539 + msgid "โœ“ Verified Constituent" 540 + msgstr "โœ“ Verified Constituent" 541 + 542 + #: letters/templates/letters/letter_detail.html:60 543 + msgid "โœ“ Verified" 544 + msgstr "โœ“ Verified" 545 + 546 + #: letters/templates/letters/letter_detail.html:70 547 + msgid "No signatures yet. Be the first to sign!" 548 + msgstr "No signatures yet. Be the first to sign!" 549 + 550 + #: letters/templates/letters/letter_detail.html:75 551 + #: letters/templates/letters/letter_detail.html:79 552 + msgid "Report this letter" 553 + msgstr "Report this letter" 554 + 555 + #: letters/templates/letters/letter_detail.html:76 556 + msgid "If you believe this letter violates our guidelines, please report it." 557 + msgstr "If you believe this letter violates our guidelines, please report it." 558 + 559 + #: letters/templates/letters/letter_detail.html:81 560 + msgid "to report this letter" 561 + msgstr "to report this letter" 562 + 563 + #: letters/templates/letters/letter_detail.html:86 564 + msgid "Back to all letters" 565 + msgstr "Back to all letters" 566 + 567 + #: letters/templates/letters/letter_form.html:4 568 + #: letters/templates/letters/representative_detail.html:143 569 + msgid "Write a Letter" 570 + msgstr "Write a Letter" 571 + 572 + #: letters/templates/letters/letter_form.html:10 573 + msgid "Write an Open Letter" 574 + msgstr "Write an Open Letter" 575 + 576 + #: letters/templates/letters/letter_form.html:12 577 + msgid "" 578 + "Write an open letter to a political representative. Your letter will be " 579 + "published publicly so others can read and sign it." 580 + msgstr "" 581 + "Write an open letter to a political representative. Your letter will be " 582 + "published publicly so others can read and sign it." 583 + 584 + #: letters/templates/letters/letter_form.html:16 585 + msgid "Before you write" 586 + msgstr "Before you write" 587 + 588 + #: letters/templates/letters/letter_form.html:18 589 + msgid "Be thoughtful but feel free to be critical." 590 + msgstr "Be thoughtful but feel free to be critical." 591 + 592 + #: letters/templates/letters/letter_form.html:19 593 + msgid "Representatives are humans tooโ€”stay respectful." 594 + msgstr "Representatives are humans tooโ€”stay respectful." 595 + 596 + #: letters/templates/letters/letter_form.html:20 597 + msgid "Keep your arguments clear and concise." 598 + msgstr "Keep your arguments clear and concise." 599 + 600 + #: letters/templates/letters/letter_form.html:21 601 + msgid "No insults or hate speech." 602 + msgstr "No insults or hate speech." 603 + 604 + #: letters/templates/letters/letter_form.html:22 605 + msgid "Stay within the bounds of the Grundgesetz when making demands." 606 + msgstr "Stay within the bounds of the Grundgesetz when making demands." 607 + 608 + #: letters/templates/letters/letter_form.html:30 609 + msgid "Title:" 610 + msgstr "Title:" 611 + 612 + #: letters/templates/letters/letter_form.html:36 613 + msgid "" 614 + "Describe your concern in a sentence; we'll use it to suggest representatives." 615 + msgstr "" 616 + "Describe your concern in a sentence; we'll use it to suggest representatives." 617 + 618 + #: letters/templates/letters/letter_form.html:41 619 + msgid "To Representative:" 620 + msgstr "To Representative:" 621 + 622 + #: letters/templates/letters/letter_form.html:47 623 + msgid "" 624 + "Already know who to address? Pick them here. Otherwise, use the suggestions " 625 + "below." 626 + msgstr "" 627 + "Already know who to address? Pick them here. Otherwise, use the suggestions " 628 + "below." 629 + 630 + #: letters/templates/letters/letter_form.html:53 631 + msgid "Letter Body:" 632 + msgstr "Letter Body:" 633 + 634 + #: letters/templates/letters/letter_form.html:60 635 + msgid "Publish Letter" 636 + msgstr "Publish Letter" 637 + 638 + #: letters/templates/letters/letter_form.html:67 639 + msgid "Smart Suggestions" 640 + msgstr "Smart Suggestions" 641 + 642 + #: letters/templates/letters/letter_form.html:69 643 + msgid "" 644 + "Type your title and we'll use your verified profile to suggest " 645 + "representatives, topics, and related letters." 646 + msgstr "" 647 + "Type your title and we'll use your verified profile to suggest " 648 + "representatives, topics, and related letters." 649 + 650 + #: letters/templates/letters/letter_form.html:81 651 + msgid "Loading..." 652 + msgstr "Loading..." 653 + 654 + #: letters/templates/letters/letter_form.html:83 655 + msgid "Analyzing your title..." 656 + msgstr "Analyzing your title..." 657 + 658 + #: letters/templates/letters/letter_list.html:4 659 + msgid "Browse Letters" 660 + msgstr "Browse Letters" 661 + 662 + #: letters/templates/letters/letter_list.html:8 663 + msgid "About This" 664 + msgstr "About This" 665 + 666 + #: letters/templates/letters/letter_list.html:9 667 + msgid "" 668 + "Make your voice heard, reach out to your representative, participate in " 669 + "democracy." 670 + msgstr "" 671 + "Make your voice heard, reach out to your representative, participate in " 672 + "democracy." 673 + 674 + #: letters/templates/letters/letter_list.html:10 675 + msgid "Open letters authored and signed by fellow citizens." 676 + msgstr "Open letters authored and signed by fellow citizens." 677 + 678 + #: letters/templates/letters/letter_list.html:11 679 + msgid "" 680 + "Physical letters mailed to representatives when number of verified " 681 + "signatures > 100." 682 + msgstr "" 683 + "Physical letters mailed to representatives when number of verified " 684 + "signatures > 100." 685 + 686 + #: letters/templates/letters/letter_list.html:15 687 + msgid "Browse Open Letters" 688 + msgstr "Browse Open Letters" 689 + 690 + #: letters/templates/letters/letter_list.html:25 691 + msgid "Popular tags:" 692 + msgstr "Popular tags:" 693 + 694 + #: letters/templates/letters/letter_list.html:41 695 + msgid "Previous" 696 + msgstr "Previous" 697 + 698 + #: letters/templates/letters/letter_list.html:43 699 + #, python-format 700 + msgid "Page %(page)s of %(total)s" 701 + msgstr "Page %(page)s of %(total)s" 702 + 703 + #: letters/templates/letters/letter_list.html:45 704 + msgid "Next" 705 + msgstr "Next" 706 + 707 + #: letters/templates/letters/letter_list.html:51 708 + msgid "No letters found." 709 + msgstr "No letters found." 710 + 711 + #: letters/templates/letters/letter_list.html:51 712 + msgid "Be the first to write one!" 713 + msgstr "Be the first to write one!" 714 + 715 + #: letters/templates/letters/login.html:14 716 + #: letters/templates/letters/register.html:15 717 + msgid "Username:" 718 + msgstr "Username:" 719 + 720 + #: letters/templates/letters/login.html:22 721 + #: letters/templates/letters/register.html:47 722 + msgid "Password:" 723 + msgstr "Password:" 724 + 725 + #: letters/templates/letters/login.html:37 726 + msgid "Forgot your password?" 727 + msgstr "Forgot your password?" 728 + 729 + #: letters/templates/letters/login.html:41 730 + msgid "Don't have an account?" 731 + msgstr "Don't have an account?" 732 + 733 + #: letters/templates/letters/login.html:41 734 + msgid "Register here" 735 + msgstr "Register here" 736 + 737 + #: letters/templates/letters/partials/letter_card.html:6 738 + msgid "To" 739 + msgstr "To" 740 + 741 + #: letters/templates/letters/partials/letter_card.html:20 742 + #, python-format 743 + msgid "%(counter)s signature" 744 + msgid_plural "%(counter)s signatures" 745 + msgstr[0] "%(counter)s signature" 746 + msgstr[1] "%(counter)s signatures" 747 + 748 + #: letters/templates/letters/partials/letter_card.html:20 749 + #, python-format 750 + msgid "%(counter)s verified" 751 + msgid_plural "%(counter)s verified" 752 + msgstr[0] "%(counter)s verified" 753 + msgstr[1] "%(counter)s verified" 754 + 755 + #: letters/templates/letters/partials/representative_card.html:21 756 + #: letters/templates/letters/partials/representative_card.html:23 757 + msgid "Constituency" 758 + msgstr "Constituency" 759 + 760 + #: letters/templates/letters/partials/representative_card.html:27 761 + msgid "Mandate" 762 + msgstr "Mandate" 763 + 764 + #: letters/templates/letters/partials/representative_card.html:34 765 + msgid "Focus" 766 + msgstr "Focus" 767 + 768 + #: letters/templates/letters/partials/representative_card.html:34 769 + msgid "self-declared" 770 + msgstr "self-declared" 771 + 772 + #: letters/templates/letters/partials/representative_card.html:45 773 + msgid "Committees" 774 + msgstr "Committees" 775 + 776 + #: letters/templates/letters/partials/representative_card.html:57 777 + msgid "Email" 778 + msgstr "Email" 779 + 780 + #: letters/templates/letters/partials/representative_card.html:60 781 + msgid "Website" 782 + msgstr "Website" 783 + 784 + #: letters/templates/letters/partials/representative_card.html:66 785 + msgid "View profile" 786 + msgstr "View profile" 787 + 788 + #: letters/templates/letters/partials/suggestions.html:10 789 + msgid "" 790 + "We couldn't match you to a constituency yet. Update your profile " 791 + "verification to see local representatives." 792 + msgstr "" 793 + "We couldn't match you to a constituency yet. Update your profile " 794 + "verification to see local representatives." 795 + 796 + #: letters/templates/letters/partials/suggestions.html:16 797 + msgid "Our Interpretation" 798 + msgstr "Our Interpretation" 799 + 800 + #: letters/templates/letters/partials/suggestions.html:21 801 + msgid "Topic:" 802 + msgstr "Topic:" 803 + 804 + #: letters/templates/letters/partials/suggestions.html:28 805 + msgid "No specific policy area detected. Try adding more keywords." 806 + msgstr "No specific policy area detected. Try adding more keywords." 807 + 808 + #: letters/templates/letters/partials/suggestions.html:37 809 + msgid "Related Keywords" 810 + msgstr "Related Keywords" 811 + 812 + #: letters/templates/letters/partials/suggestions.html:51 813 + msgid "Your Direct Representatives" 814 + msgstr "Your Direct Representatives" 815 + 816 + #: letters/templates/letters/partials/suggestions.html:56 817 + msgid "These representatives directly represent your constituency:" 818 + msgstr "These representatives directly represent your constituency:" 819 + 820 + #: letters/templates/letters/partials/suggestions.html:67 821 + #: letters/templates/letters/partials/suggestions.html:102 822 + msgid "Select" 823 + msgstr "Select" 824 + 825 + #: letters/templates/letters/partials/suggestions.html:81 826 + msgid "Topic Experts" 827 + msgstr "Topic Experts" 828 + 829 + #: letters/templates/letters/partials/suggestions.html:86 830 + #, python-format 831 + msgid "" 832 + "These representatives are experts on \"%(topic)s\" based on their committee " 833 + "memberships:" 834 + msgstr "" 835 + "These representatives are experts on \"%(topic)s\" based on their committee " 836 + "memberships:" 837 + 838 + #: letters/templates/letters/partials/suggestions.html:95 839 + msgid "of" 840 + msgstr "of" 841 + 842 + #: letters/templates/letters/partials/suggestions.html:116 843 + msgid "Suggested Representatives" 844 + msgstr "Suggested Representatives" 845 + 846 + #: letters/templates/letters/partials/suggestions.html:119 847 + msgid "" 848 + "No representatives found. Representatives may need to be synced for this " 849 + "governmental level." 850 + msgstr "" 851 + "No representatives found. Representatives may need to be synced for this " 852 + "governmental level." 853 + 854 + #: letters/templates/letters/partials/suggestions.html:148 855 + msgid "Selected:" 856 + msgstr "Selected:" 857 + 858 + #: letters/templates/letters/password_reset_complete.html:4 859 + #: letters/templates/letters/password_reset_complete.html:8 860 + msgid "Password updated" 861 + msgstr "Password updated" 862 + 863 + #: letters/templates/letters/password_reset_complete.html:9 864 + msgid "You can now sign in using your new password." 865 + msgstr "You can now sign in using your new password." 866 + 867 + #: letters/templates/letters/password_reset_complete.html:10 868 + msgid "Go to login" 869 + msgstr "Go to login" 870 + 871 + #: letters/templates/letters/password_reset_confirm.html:4 872 + #: letters/templates/letters/password_reset_confirm.html:9 873 + msgid "Choose a new password" 874 + msgstr "Choose a new password" 875 + 876 + #: letters/templates/letters/password_reset_confirm.html:13 877 + msgid "New password" 878 + msgstr "New password" 879 + 880 + #: letters/templates/letters/password_reset_confirm.html:20 881 + msgid "Confirm password" 882 + msgstr "Confirm password" 883 + 884 + #: letters/templates/letters/password_reset_confirm.html:26 885 + msgid "Update password" 886 + msgstr "Update password" 887 + 888 + #: letters/templates/letters/password_reset_confirm.html:29 889 + msgid "Reset link invalid" 890 + msgstr "Reset link invalid" 891 + 892 + #: letters/templates/letters/password_reset_confirm.html:30 893 + msgid "This password reset link is no longer valid. Please request a new one." 894 + msgstr "This password reset link is no longer valid. Please request a new one." 895 + 896 + #: letters/templates/letters/password_reset_confirm.html:31 897 + msgid "Request new link" 898 + msgstr "Request new link" 899 + 900 + #: letters/templates/letters/password_reset_done.html:4 901 + msgid "Reset email sent" 902 + msgstr "Reset email sent" 903 + 904 + #: letters/templates/letters/password_reset_done.html:9 905 + msgid "" 906 + "If an account exists for that email address, we just sent you instructions " 907 + "to choose a new password." 908 + msgstr "" 909 + "If an account exists for that email address, we just sent you instructions " 910 + "to choose a new password." 911 + 912 + #: letters/templates/letters/password_reset_done.html:10 913 + msgid "The link will stay valid for a limited time." 914 + msgstr "The link will stay valid for a limited time." 915 + 916 + #: letters/templates/letters/password_reset_done.html:11 917 + msgid "Back to login" 918 + msgstr "Back to login" 919 + 920 + #: letters/templates/letters/password_reset_form.html:4 921 + msgid "Reset password" 922 + msgstr "Reset password" 923 + 924 + #: letters/templates/letters/password_reset_form.html:8 925 + msgid "Reset your password" 926 + msgstr "Reset your password" 927 + 928 + #: letters/templates/letters/password_reset_form.html:9 929 + msgid "" 930 + "Enter the email address you used during registration. We will send you a " 931 + "link to create a new password." 932 + msgstr "" 933 + "Enter the email address you used during registration. We will send you a " 934 + "link to create a new password." 935 + 936 + #: letters/templates/letters/password_reset_form.html:17 937 + msgid "Send reset link" 938 + msgstr "Send reset link" 939 + 940 + #: letters/templates/letters/profile.html:13 941 + #, python-format 942 + msgid "%(username)s's Profile" 943 + msgstr "%(username)s's Profile" 944 + 945 + #: letters/templates/letters/profile.html:16 946 + msgid "Identity & Constituency" 947 + msgstr "Identity & Constituency" 948 + 949 + #: letters/templates/letters/profile.html:19 950 + msgid "Status:" 951 + msgstr "Status:" 952 + 953 + #: letters/templates/letters/profile.html:21 954 + msgid "Type:" 955 + msgstr "Type:" 956 + 957 + #: letters/templates/letters/profile.html:28 958 + msgid "" 959 + "You self-declared your constituency. Representatives will see your " 960 + "signatures as self-declared constituents." 961 + msgstr "" 962 + "You self-declared your constituency. Representatives will see your " 963 + "signatures as self-declared constituents." 964 + 965 + #: letters/templates/letters/profile.html:30 966 + msgid "Start third-party verification" 967 + msgstr "Start third-party verification" 968 + 969 + #: letters/templates/letters/profile.html:33 970 + msgid "" 971 + "Your identity was verified via a third-party provider. Signatures will " 972 + "appear as verified constituents." 973 + msgstr "" 974 + "Your identity was verified via a third-party provider. Signatures will " 975 + "appear as verified constituents." 976 + 977 + #: letters/templates/letters/profile.html:38 978 + msgid "Your verification is being processed." 979 + msgstr "Your verification is being processed." 980 + 981 + #: letters/templates/letters/profile.html:39 982 + msgid "Complete Verification (Stub)" 983 + msgstr "Complete Verification (Stub)" 984 + 985 + #: letters/templates/letters/profile.html:43 986 + msgid "Verification failed. Please try again or contact support." 987 + msgstr "Verification failed. Please try again or contact support." 988 + 989 + #: letters/templates/letters/profile.html:48 990 + msgid "" 991 + "You can self-declare your constituency below or start a verification with a " 992 + "trusted provider. Verified signatures carry more weight." 993 + msgstr "" 994 + "You can self-declare your constituency below or start a verification with a " 995 + "trusted provider. Verified signatures carry more weight." 996 + 997 + #: letters/templates/letters/profile.html:50 998 + msgid "Start Third-party Verification" 999 + msgstr "Start Third-party Verification" 1000 + 1001 + #: letters/templates/letters/profile.html:55 1002 + msgid "Ihre Adresse" 1003 + msgstr "Ihre Adresse" 1004 + 1005 + #: letters/templates/letters/profile.html:57 1006 + msgid "" 1007 + "Geben Sie Ihre vollstรคndige Adresse ein, um prรคzise Wahlkreis- und " 1008 + "Abgeordnetenempfehlungen zu erhalten." 1009 + msgstr "" 1010 + "Geben Sie Ihre vollstรคndige Adresse ein, um prรคzise Wahlkreis- und " 1011 + "Abgeordnetenempfehlungen zu erhalten." 1012 + 1013 + #: letters/templates/letters/profile.html:61 1014 + msgid "Gespeicherte Adresse:" 1015 + msgstr "Gespeicherte Adresse:" 1016 + 1017 + #: letters/templates/letters/profile.html:83 1018 + msgid "Adresse speichern" 1019 + msgstr "Adresse speichern" 1020 + 1021 + #: letters/templates/letters/profile.html:88 1022 + msgid "Self-declare your constituency" 1023 + msgstr "Self-declare your constituency" 1024 + 1025 + #: letters/templates/letters/profile.html:90 1026 + msgid "" 1027 + "Select the constituencies you live in so we can prioritise the right " 1028 + "representatives." 1029 + msgstr "" 1030 + "Select the constituencies you live in so we can prioritise the right " 1031 + "representatives." 1032 + 1033 + #: letters/templates/letters/profile.html:109 1034 + msgid "Save constituencies" 1035 + msgstr "Save constituencies" 1036 + 1037 + #: letters/templates/letters/profile.html:114 1038 + msgid "Your Letters" 1039 + msgstr "Your Letters" 1040 + 1041 + #: letters/templates/letters/profile.html:120 1042 + msgid "You haven't written any letters yet." 1043 + msgstr "You haven't written any letters yet." 1044 + 1045 + #: letters/templates/letters/profile.html:120 1046 + msgid "Write one now!" 1047 + msgstr "Write one now!" 1048 + 1049 + #: letters/templates/letters/profile.html:125 1050 + msgid "Letters You've Signed" 1051 + msgstr "Letters You've Signed" 1052 + 1053 + #: letters/templates/letters/profile.html:133 1054 + msgid "You haven't signed any letters yet." 1055 + msgstr "You haven't signed any letters yet." 1056 + 1057 + #: letters/templates/letters/profile.html:133 1058 + msgid "Browse letters" 1059 + msgstr "Browse letters" 1060 + 1061 + #: letters/templates/letters/profile.html:138 1062 + msgid "Account" 1063 + msgstr "Account" 1064 + 1065 + #: letters/templates/letters/profile.html:140 1066 + msgid "" 1067 + "Need a fresh start? You can delete your account at any time. Your letters " 1068 + "stay visible but without your name." 1069 + msgstr "" 1070 + "Need a fresh start? You can delete your account at any time. Your letters " 1071 + "stay visible but without your name." 1072 + 1073 + #: letters/templates/letters/register.html:9 1074 + msgid "" 1075 + "After registration we'll send you an email to confirm your address before " 1076 + "you can sign in." 1077 + msgstr "" 1078 + "After registration we'll send you an email to confirm your address before " 1079 + "you can sign in." 1080 + 1081 + #: letters/templates/letters/register.html:23 1082 + msgid "Email:" 1083 + msgstr "Email:" 1084 + 1085 + #: letters/templates/letters/register.html:31 1086 + msgid "First Name (optional):" 1087 + msgstr "First Name (optional):" 1088 + 1089 + #: letters/templates/letters/register.html:39 1090 + msgid "Last Name (optional):" 1091 + msgstr "Last Name (optional):" 1092 + 1093 + #: letters/templates/letters/register.html:55 1094 + msgid "Confirm Password:" 1095 + msgstr "Confirm Password:" 1096 + 1097 + #: letters/templates/letters/register.html:70 1098 + msgid "Already have an account?" 1099 + msgstr "Already have an account?" 1100 + 1101 + #: letters/templates/letters/register.html:70 1102 + msgid "Login here" 1103 + msgstr "Login here" 1104 + 1105 + #: letters/templates/letters/representative_detail.html:19 1106 + msgid "รœber" 1107 + msgstr "รœber" 1108 + 1109 + #: letters/templates/letters/representative_detail.html:30 1110 + msgid "Committee Memberships" 1111 + msgstr "Committee Memberships" 1112 + 1113 + #: letters/templates/letters/representative_detail.html:75 1114 + msgid "Open Letters" 1115 + msgstr "Open Letters" 1116 + 1117 + #: letters/templates/letters/representative_detail.html:83 1118 + msgid "No letters have been written to this representative yet." 1119 + msgstr "No letters have been written to this representative yet." 1120 + 1121 + #: letters/templates/letters/representative_detail.html:85 1122 + msgid "Write the First Letter" 1123 + msgstr "Write the First Letter" 1124 + 1125 + #: letters/templates/letters/representative_detail.html:95 1126 + msgid "External Resources" 1127 + msgstr "External Resources" 1128 + 1129 + #: letters/templates/letters/representative_detail.html:100 1130 + msgid "Abgeordnetenwatch Profile" 1131 + msgstr "Abgeordnetenwatch Profile" 1132 + 1133 + #: letters/templates/letters/representative_detail.html:102 1134 + msgid "" 1135 + "View voting record, questions, and detailed profile on Abgeordnetenwatch.de" 1136 + msgstr "" 1137 + "View voting record, questions, and detailed profile on Abgeordnetenwatch.de" 1138 + 1139 + #: letters/templates/letters/representative_detail.html:112 1140 + msgid "Wikipedia Article" 1141 + msgstr "Wikipedia Article" 1142 + 1143 + #: letters/templates/letters/representative_detail.html:114 1144 + msgid "Read more about this representative on Wikipedia" 1145 + msgstr "Read more about this representative on Wikipedia" 1146 + 1147 + #: letters/templates/letters/representative_detail.html:117 1148 + msgid "View on Wikipedia" 1149 + msgstr "View on Wikipedia" 1150 + 1151 + #: letters/templates/letters/representative_detail.html:123 1152 + msgid "No external resources available for this representative." 1153 + msgstr "No external resources available for this representative." 1154 + 1155 + #: letters/templates/letters/representative_detail.html:130 1156 + msgid "Kontakt" 1157 + msgstr "Kontakt" 1158 + 1159 + #: letters/templates/letters/representative_detail.html:145 1160 + #, python-format 1161 + msgid "Start a new open letter to %(name)s" 1162 + msgstr "Start a new open letter to %(name)s" 1163 + 1164 + #: letters/templates/letters/representative_detail.html:153 1165 + msgid "Login to Write Letter" 1166 + msgstr "Login to Write Letter" 1167 + 1168 + #: letters/views.py:52 1169 + msgid "Confirm your WriteThem.eu account" 1170 + msgstr "Confirm your WriteThem.eu account" 1171 + 1172 + #: letters/views.py:184 1173 + msgid "Your letter has been published and your signature has been added!" 1174 + msgstr "Your letter has been published and your signature has been added!" 1175 + 1176 + #: letters/views.py:197 1177 + msgid "You have already signed this letter." 1178 + msgstr "You have already signed this letter." 1179 + 1180 + #: letters/views.py:207 1181 + msgid "Your signature has been added!" 1182 + msgstr "Your signature has been added!" 1183 + 1184 + #: letters/views.py:225 1185 + msgid "Thank you for your report. Our team will review it." 1186 + msgstr "Thank you for your report. Our team will review it." 1187 + 1188 + #: letters/views.py:259 1189 + msgid "" 1190 + "Please confirm your email address. We sent you a link to activate your " 1191 + "account." 1192 + msgstr "" 1193 + "Please confirm your email address. We sent you a link to activate your " 1194 + "account." 1195 + 1196 + #: letters/views.py:289 1197 + msgid "Your account has been activated. You can now log in." 1198 + msgstr "Your account has been activated. You can now log in." 1199 + 1200 + #: letters/views.py:292 1201 + msgid "Your account is already active." 1202 + msgstr "Your account is already active." 1203 + 1204 + #: letters/views.py:347 1205 + msgid "Ihre Adresse wurde gespeichert." 1206 + msgstr "Ihre Adresse wurde gespeichert." 1207 + 1208 + #: letters/views.py:363 1209 + msgid "Your constituency information has been updated." 1210 + msgstr "Your constituency information has been updated." 1211 + 1212 + #: letters/views.py:391 1213 + msgid "" 1214 + "Your account has been deleted. Your published letters remain available to " 1215 + "the public." 1216 + msgstr "" 1217 + "Your account has been deleted. Your published letters remain available to " 1218 + "the public."
+11 -1
website/writethem/settings.py
··· 43 MIDDLEWARE = [ 44 'django.middleware.security.SecurityMiddleware', 45 'django.contrib.sessions.middleware.SessionMiddleware', 46 'django.middleware.common.CommonMiddleware', 47 'django.middleware.csrf.CsrfViewMiddleware', 48 'django.contrib.auth.middleware.AuthenticationMiddleware', ··· 105 # https://docs.djangoproject.com/en/5.2/topics/i18n/ 106 107 LANGUAGE_CODE = 'de' 108 109 TIME_ZONE = 'Europe/Berlin' 110 111 - USE_I18N = False 112 USE_L10N = True 113 114 USE_TZ = True 115 116 117 # Static files (CSS, JavaScript, Images)
··· 43 MIDDLEWARE = [ 44 'django.middleware.security.SecurityMiddleware', 45 'django.contrib.sessions.middleware.SessionMiddleware', 46 + 'django.middleware.locale.LocaleMiddleware', 47 'django.middleware.common.CommonMiddleware', 48 'django.middleware.csrf.CsrfViewMiddleware', 49 'django.contrib.auth.middleware.AuthenticationMiddleware', ··· 106 # https://docs.djangoproject.com/en/5.2/topics/i18n/ 107 108 LANGUAGE_CODE = 'de' 109 + LANGUAGES = [ 110 + ('de', 'Deutsch'), 111 + ('en', 'English'), 112 + ] 113 114 TIME_ZONE = 'Europe/Berlin' 115 116 + USE_I18N = True 117 USE_L10N = True 118 119 USE_TZ = True 120 + 121 + # Locale paths - where Django looks for .po files 122 + LOCALE_PATHS = [ 123 + BASE_DIR / 'locale', 124 + ] 125 126 127 # Static files (CSS, JavaScript, Images)
+12 -3
website/writethem/urls.py
··· 14 1. Import the include() function: from django.urls import include, path 15 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 """ 17 from django.conf import settings 18 from django.conf.urls.static import static 19 - from django.contrib import admin 20 - from django.urls import include, path 21 22 urlpatterns = [ 23 path('admin/', admin.site.urls), 24 path('', include('letters.urls')), 25 - ] 26 27 if settings.DEBUG: 28 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
··· 14 1. Import the include() function: from django.urls import include, path 15 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 """ 17 + from django.contrib import admin 18 + from django.urls import path, include 19 from django.conf import settings 20 from django.conf.urls.static import static 21 + from django.conf.urls.i18n import i18n_patterns 22 + from django.views.i18n import set_language 23 24 urlpatterns = [ 25 + # Language switcher endpoint (no prefix) 26 + path('i18n/setlang/', set_language, name='set_language'), 27 + ] 28 + 29 + # All user-facing URLs get language prefix 30 + urlpatterns += i18n_patterns( 31 path('admin/', admin.site.urls), 32 path('', include('letters.urls')), 33 + prefix_default_language=True, 34 + ) 35 36 if settings.DEBUG: 37 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)