From b1b5a205699a48351de76eed289af06a87873c1b Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:09:45 -0700 Subject: [PATCH 001/156] Add examples/scripts folder for GrampyScript with Open-dialog previews Ships 10 worked example .gram.py scripts in a shared scripts/ folder that also serves as the default Open/Save location, so a user's own scripts naturally collect next to them. Descriptions live in a translatable script_descriptions.py module (picked up by the addon's existing xgettext pipeline) and are shown as a preview when browsing the Open dialog, with a fallback to a script's own leading comment for uncatalogued files. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 57 +++++++++ GrampyScript/MANIFEST | 2 + GrampyScript/script_descriptions.py | 113 ++++++++++++++++++ GrampyScript/scripts/01_list_people.gram.py | 9 ++ .../scripts/02_filter_by_surname.gram.py | 7 ++ .../scripts/03_family_overview.gram.py | 4 + .../scripts/04_gender_pie_chart.gram.py | 7 ++ GrampyScript/scripts/05_age_histogram.gram.py | 9 ++ .../06_mark_unsourced_people_private.gram.py | 12 ++ .../scripts/07_csv_ready_report.gram.py | 14 +++ .../scripts/08_active_person_summary.gram.py | 13 ++ .../scripts/09_selected_people_report.gram.py | 4 + .../10_find_missing_birth_dates.gram.py | 5 + GrampyScript/scripts/README.md | 27 +++++ .../tests/test_extract_header_comment.py | 72 +++++++++++ .../tests/test_script_descriptions.py | 55 +++++++++ 16 files changed, 410 insertions(+) create mode 100644 GrampyScript/MANIFEST create mode 100644 GrampyScript/script_descriptions.py create mode 100644 GrampyScript/scripts/01_list_people.gram.py create mode 100644 GrampyScript/scripts/02_filter_by_surname.gram.py create mode 100644 GrampyScript/scripts/03_family_overview.gram.py create mode 100644 GrampyScript/scripts/04_gender_pie_chart.gram.py create mode 100644 GrampyScript/scripts/05_age_histogram.gram.py create mode 100644 GrampyScript/scripts/06_mark_unsourced_people_private.gram.py create mode 100644 GrampyScript/scripts/07_csv_ready_report.gram.py create mode 100644 GrampyScript/scripts/08_active_person_summary.gram.py create mode 100644 GrampyScript/scripts/09_selected_people_report.gram.py create mode 100644 GrampyScript/scripts/10_find_missing_birth_dates.gram.py create mode 100644 GrampyScript/scripts/README.md create mode 100644 GrampyScript/tests/test_extract_header_comment.py create mode 100644 GrampyScript/tests/test_script_descriptions.py diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index ace7a5645..852b92aea 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -57,6 +57,7 @@ EditSource, ) from datadict2 import DataDict2, NoneData, set_sa +from script_descriptions import SCRIPT_DESCRIPTIONS _ = glocale.translation.gettext @@ -87,6 +88,27 @@ def get_columns(source, func_name): return [] +def extract_header_comment(source): + """ + Extract the leading '#'-comment block of a script as plain text, + for use as a fallback preview when a file has no catalogued + description in SCRIPT_DESCRIPTIONS. + """ + lines = [] + for line in source.splitlines(): + stripped = line.strip() + if stripped.startswith("#"): + lines.append(stripped.lstrip("#").strip()) + elif stripped == "" and not lines: + continue + else: + break + return "\n".join(lines).strip() + + +SCRIPTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "scripts") + + class ScriptOpenFileChooserDialog(Gtk.FileChooserDialog): def __init__(self, uistate): # type: (DisplayState) -> None @@ -116,6 +138,37 @@ def __init__(self, uistate): filter_all.add_pattern("*.*") self.add_filter(filter_all) + self.preview_label = Gtk.Label() + self.preview_label.set_line_wrap(True) + self.preview_label.set_xalign(0) + self.preview_label.set_yalign(0) + preview_scrolled = Gtk.ScrolledWindow() + preview_scrolled.set_size_request(220, -1) + preview_scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + preview_scrolled.add(self.preview_label) + preview_scrolled.show_all() + self.set_preview_widget(preview_scrolled) + self.connect("update-preview", self.on_update_preview) + + def on_update_preview(self, dialog): + filename = dialog.get_preview_filename() + text = "" + if filename and filename.endswith(".gram.py") and os.path.isfile(filename): + basename = os.path.basename(filename) + if basename in SCRIPT_DESCRIPTIONS: + title, description = SCRIPT_DESCRIPTIONS[basename] + text = "%s\n\n%s" % (title, description) + else: + try: + text = extract_header_comment(open(filename).read()) + except Exception: + text = "" + if text: + self.preview_label.set_text(text) + dialog.set_preview_widget_active(True) + else: + dialog.set_preview_widget_active(False) + class ScriptSaveFileChooserDialog(Gtk.FileChooserDialog): def __init__(self, uistate): @@ -448,6 +501,8 @@ def open_script(self, widget): choose_file_dialog = ScriptOpenFileChooserDialog(self.uistate) if self.last_filename: choose_file_dialog.set_filename(self.last_filename) + elif os.path.isdir(SCRIPTS_DIR): + choose_file_dialog.set_current_folder(SCRIPTS_DIR) while True: response = choose_file_dialog.run() @@ -483,6 +538,8 @@ def save_as_script(self, widget): choose_file_dialog.set_do_overwrite_confirmation(True) if self.last_filename: choose_file_dialog.set_filename(self.last_filename) + elif os.path.isdir(SCRIPTS_DIR): + choose_file_dialog.set_current_folder(SCRIPTS_DIR) while True: response = choose_file_dialog.run() diff --git a/GrampyScript/MANIFEST b/GrampyScript/MANIFEST new file mode 100644 index 000000000..09e11f74c --- /dev/null +++ b/GrampyScript/MANIFEST @@ -0,0 +1,2 @@ +GrampyScript/scripts/*.gram.py +GrampyScript/scripts/README.md diff --git a/GrampyScript/script_descriptions.py b/GrampyScript/script_descriptions.py new file mode 100644 index 000000000..fbbdccef8 --- /dev/null +++ b/GrampyScript/script_descriptions.py @@ -0,0 +1,113 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Translatable titles and descriptions for the bundled example scripts in +scripts/. Kept out of the .gram.py files themselves so the examples stay +free of gettext markup while still being picked up by the addon's normal +xgettext-based translation pipeline (see ../make.py). +""" + +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.translation.gettext + +SCRIPT_DESCRIPTIONS = { + "01_list_people.gram.py": ( + _("List All People"), + _( + "Iterate over every person in the database and show their " + "Gramps ID, given name, surname, and gender in the results " + "table." + ), + ), + "02_filter_by_surname.gram.py": ( + _("Filter By Surname"), + _( + "List only the people whose surname matches a given value — " + "a starting point for narrowing any report down by a " + "condition." + ), + ), + "03_family_overview.gram.py": ( + _("Family Overview"), + _( + "List every family together with the father, the mother, and " + "how many children they have — a quick way to spot families " + "that look incomplete." + ), + ), + "04_gender_pie_chart.gram.py": ( + _("Gender Breakdown (Pie Chart)"), + _( + "Count how many people are male, female, or of unknown " + "gender, then draw a pie chart of the totals. Check the " + "Chart tab after running." + ), + ), + "05_age_histogram.gram.py": ( + _("Age At Death Histogram"), + _( + "For everyone with both a birth and a death event recorded, " + "compute their age in whole years and draw a histogram of " + "the distribution. Check the Chart tab after running." + ), + ), + "06_mark_unsourced_people_private.gram.py": ( + _("Mark Unsourced People As Private"), + _( + "Batch-edit example: find every person who has no citations " + "attached and flag them as private, wrapped in " + "begin_changes()/end_changes() so the edits happen inside a " + "single, undoable transaction." + ), + ), + "07_csv_ready_report.gram.py": ( + _("CSV-Ready People Report"), + _( + "Build a simple tabular report — ID, name, gender, birth " + "year — for every person. Once it runs, use Data > Save as " + "CSV or Copy to clipboard to export the Table tab's " + "contents." + ), + ), + "08_active_person_summary.gram.py": ( + _("Active Person Summary"), + _( + "Show a compact family summary for the currently active " + "person: their record, parents, spouse, and children." + ), + ), + "09_selected_people_report.gram.py": ( + _("Report On Selected People"), + _( + "List just the people currently selected (highlighted) in " + "the People view. Select some rows in the People view " + "before running this script." + ), + ), + "10_find_missing_birth_dates.gram.py": ( + _("Find People Missing A Birth Date"), + _( + "Data-quality check: list every person who has no recorded " + "birth event, so you can prioritize research on those " + "records." + ), + ), +} diff --git a/GrampyScript/scripts/01_list_people.gram.py b/GrampyScript/scripts/01_list_people.gram.py new file mode 100644 index 000000000..90d3fa04c --- /dev/null +++ b/GrampyScript/scripts/01_list_people.gram.py @@ -0,0 +1,9 @@ +# List All People + +for person in people(): + row( + person.gramps_id, + person.name.first_name, + person.surname.surname, + person.gender, + ) diff --git a/GrampyScript/scripts/02_filter_by_surname.gram.py b/GrampyScript/scripts/02_filter_by_surname.gram.py new file mode 100644 index 000000000..a1f64deee --- /dev/null +++ b/GrampyScript/scripts/02_filter_by_surname.gram.py @@ -0,0 +1,7 @@ +# Filter By Surname + +TARGET_SURNAME = "Smith" + +for person in people(): + if person.surname.surname == TARGET_SURNAME: + row(person.gramps_id, person.name.first_name, person.surname.surname) diff --git a/GrampyScript/scripts/03_family_overview.gram.py b/GrampyScript/scripts/03_family_overview.gram.py new file mode 100644 index 000000000..9574b0a97 --- /dev/null +++ b/GrampyScript/scripts/03_family_overview.gram.py @@ -0,0 +1,4 @@ +# Family Overview + +for family in families(): + row(family.gramps_id, family.father, family.mother, len(family.children)) diff --git a/GrampyScript/scripts/04_gender_pie_chart.gram.py b/GrampyScript/scripts/04_gender_pie_chart.gram.py new file mode 100644 index 000000000..cf70b8008 --- /dev/null +++ b/GrampyScript/scripts/04_gender_pie_chart.gram.py @@ -0,0 +1,7 @@ +# Gender Breakdown (Pie Chart) + +counts = counter() +for person in people(): + counts[person.gender] += 1 + +chart("pie", counts) diff --git a/GrampyScript/scripts/05_age_histogram.gram.py b/GrampyScript/scripts/05_age_histogram.gram.py new file mode 100644 index 000000000..311f7b826 --- /dev/null +++ b/GrampyScript/scripts/05_age_histogram.gram.py @@ -0,0 +1,9 @@ +# Age At Death Histogram + +ages = [] +for person in people(): + age = person.age + if age: + ages.append(age.tuple()[0]) + +chart("histogram", ages, count=15) diff --git a/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py b/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py new file mode 100644 index 000000000..65dd9855b --- /dev/null +++ b/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py @@ -0,0 +1,12 @@ +# Mark Unsourced People As Private + +begin_changes("Mark unsourced people as private") + +count = 0 +for person in people(): + if len(person.citations) == 0 and not person.private: + person.private = True + count += 1 + +end_changes() +print("Marked %d people as private" % count) diff --git a/GrampyScript/scripts/07_csv_ready_report.gram.py b/GrampyScript/scripts/07_csv_ready_report.gram.py new file mode 100644 index 000000000..2a0867b37 --- /dev/null +++ b/GrampyScript/scripts/07_csv_ready_report.gram.py @@ -0,0 +1,14 @@ +# CSV-Ready People Report + +columns("ID", "Given Name", "Surname", "Gender", "Birth Year") + +for person in people(): + birth = person.birth + birth_year = birth.get_date_object().get_year() if birth else "" + row( + person.gramps_id, + person.name.first_name, + person.surname.surname, + person.gender, + birth_year, + ) diff --git a/GrampyScript/scripts/08_active_person_summary.gram.py b/GrampyScript/scripts/08_active_person_summary.gram.py new file mode 100644 index 000000000..d0c2b7cb6 --- /dev/null +++ b/GrampyScript/scripts/08_active_person_summary.gram.py @@ -0,0 +1,13 @@ +# Active Person Summary + +person = active_person +if person: + row(person) + for parent in person.parents: + row(parent) + if person.spouse: + row(person.spouse) + for child in person.children: + row(child) +else: + print("No active person is set.") diff --git a/GrampyScript/scripts/09_selected_people_report.gram.py b/GrampyScript/scripts/09_selected_people_report.gram.py new file mode 100644 index 000000000..2d6084c6d --- /dev/null +++ b/GrampyScript/scripts/09_selected_people_report.gram.py @@ -0,0 +1,4 @@ +# Report On Selected People + +for person in selected("Person"): + row(person) diff --git a/GrampyScript/scripts/10_find_missing_birth_dates.gram.py b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py new file mode 100644 index 000000000..8e8b91dcf --- /dev/null +++ b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py @@ -0,0 +1,5 @@ +# Find People Missing A Birth Date + +for person in people(): + if not person.birth: + row(person.gramps_id, person.name.first_name, person.surname.surname) diff --git a/GrampyScript/scripts/README.md b/GrampyScript/scripts/README.md new file mode 100644 index 000000000..294c1d2f4 --- /dev/null +++ b/GrampyScript/scripts/README.md @@ -0,0 +1,27 @@ +# Gram.py Script examples + +This folder ships with the GrampyScript addon and doubles as the default +folder for Script > Open... and Script > Save as... in the gramplet. The +numbered files below are examples; anything else you save here is yours. + +| File | Description | +| --- | --- | +| `01_list_people.gram.py` | List every person with ID, given name, surname, and gender. | +| `02_filter_by_surname.gram.py` | List only people whose surname matches a given value. | +| `03_family_overview.gram.py` | List every family with father, mother, and child count. | +| `04_gender_pie_chart.gram.py` | Pie chart of the gender breakdown of everyone in the tree. | +| `05_age_histogram.gram.py` | Histogram of age at death, for people with both birth and death recorded. | +| `06_mark_unsourced_people_private.gram.py` | Batch-edit example: mark people with no citations as private. | +| `07_csv_ready_report.gram.py` | Tabular report meant to be exported via Data > Save as CSV. | +| `08_active_person_summary.gram.py` | Summary of the active person plus their parents, spouse, and children. | +| `09_selected_people_report.gram.py` | Report on just the rows currently selected in the People view. | +| `10_find_missing_birth_dates.gram.py` | Data-quality check: people with no recorded birth event. | + +Each script's title and description are also shown as a preview when you +highlight it in the Open dialog. + +**Note:** addon updates only overwrite files that share a name with +something in the released package. It's safe to save your own scripts in +this folder under any other name — just avoid editing the numbered +examples above in place, since a future addon update could overwrite those +edits. diff --git a/GrampyScript/tests/test_extract_header_comment.py b/GrampyScript/tests/test_extract_header_comment.py new file mode 100644 index 000000000..dd9d2168e --- /dev/null +++ b/GrampyScript/tests/test_extract_header_comment.py @@ -0,0 +1,72 @@ +""" +Tests for the extract_header_comment() utility from GrampyScript. + +extract_header_comment() pulls the leading '#'-comment block out of a +script's source, for use as a fallback Open-dialog preview when a file +has no catalogued entry in SCRIPT_DESCRIPTIONS. It is pure ast/string +handling -- no GTK or Gramps imports required -- so, like get_columns, it +is tested here by re-importing it directly from the module source via +ast, without pulling in the full (GTK-dependent) GrampyScript module. +""" + +import ast +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +_SOURCE = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "GrampyScript.py" +) + + +def _load_extract_header_comment(): + src = open(_SOURCE).read() + tree = ast.parse(src) + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name == "extract_header_comment": + snippet = ast.unparse(node) + ns = {} + exec(snippet, ns) + return ns["extract_header_comment"] + raise RuntimeError("extract_header_comment not found in GrampyScript.py") + + +extract_header_comment = _load_extract_header_comment() + + +class TestExtractHeaderComment(unittest.TestCase): + def test_single_line_header(self): + source = "# Title\n\nfor p in people():\n row(p)\n" + self.assertEqual(extract_header_comment(source), "Title") + + def test_multi_line_header(self): + source = "# Title\n#\n# A longer description.\n\nrow(1)\n" + self.assertEqual( + extract_header_comment(source), "Title\n\nA longer description." + ) + + def test_no_header_returns_empty(self): + source = "for p in people():\n row(p)\n" + self.assertEqual(extract_header_comment(source), "") + + def test_leading_blank_lines_before_header_are_skipped(self): + source = "\n\n# Title\n\nrow(1)\n" + self.assertEqual(extract_header_comment(source), "Title") + + def test_stops_at_first_code_line(self): + source = "# Title\nrow(1) # not part of the header\n" + self.assertEqual(extract_header_comment(source), "Title") + + def test_empty_source_returns_empty(self): + self.assertEqual(extract_header_comment(""), "") + + def test_hash_only_lines_become_blank_lines(self): + source = "# Title\n#\n# More.\n" + self.assertEqual(extract_header_comment(source), "Title\n\nMore.") + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampyScript/tests/test_script_descriptions.py b/GrampyScript/tests/test_script_descriptions.py new file mode 100644 index 000000000..77284fd20 --- /dev/null +++ b/GrampyScript/tests/test_script_descriptions.py @@ -0,0 +1,55 @@ +""" +Consistency checks between scripts/*.gram.py and SCRIPT_DESCRIPTIONS. + +script_descriptions.py has no GTK dependency (only gramps.gen.const), so +it can be imported directly here, unlike GrampyScript.py itself. +""" + +import ast +import glob +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from script_descriptions import SCRIPT_DESCRIPTIONS + +SCRIPTS_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts" +) + + +def _script_basenames(): + return { + os.path.basename(path) + for path in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")) + } + + +class TestScriptDescriptionsCoverage(unittest.TestCase): + def test_every_script_has_a_description(self): + missing = _script_basenames() - set(SCRIPT_DESCRIPTIONS) + self.assertEqual(missing, set(), "scripts missing from SCRIPT_DESCRIPTIONS") + + def test_no_stale_entries(self): + stale = set(SCRIPT_DESCRIPTIONS) - _script_basenames() + self.assertEqual(stale, set(), "SCRIPT_DESCRIPTIONS keys with no matching file") + + def test_entries_have_title_and_description(self): + for name, entry in SCRIPT_DESCRIPTIONS.items(): + self.assertEqual(len(entry), 2, name) + title, description = entry + self.assertTrue(title.strip(), "%s has an empty title" % name) + self.assertTrue(description.strip(), "%s has an empty description" % name) + + +class TestScriptsAreValidPython(unittest.TestCase): + def test_all_scripts_parse(self): + for path in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")): + with self.subTest(path=path): + ast.parse(open(path).read()) + + +if __name__ == "__main__": + unittest.main() From b4d78019a6ae242df755cd335e69091325b225c9 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:16:40 -0700 Subject: [PATCH 002/156] Move Execute button above the results notebook Places it directly below the script editor instead of below the Table/Output/Chart tabs, so it's closer to where the script is written. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 852b92aea..b2797eea9 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -435,6 +435,23 @@ def build_gui(self): widget.pack_start(self.editor, True, True, 0) + bbox = Gtk.ButtonBox() + self.apply_button = Gtk.Button(label=_("Execute ")) + self.apply_button.connect("clicked", self.apply_clicked) + self.apply_button.set_tooltip_text(_("Execute the script")) + css = b"* {background: #00aa00; color: white}" + provider = Gtk.CssProvider() + try: + provider.load_from_data(css) + self.apply_button.get_style_context().add_provider( + provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + except: + pass + + bbox.pack_start(self.apply_button, False, False, 6) + widget.pack_start(bbox, False, False, 6) + self.notebook = Gtk.Notebook() self.page1 = Gtk.ScrolledWindow() @@ -455,23 +472,6 @@ def build_gui(self): widget.pack_start(self.notebook, True, True, 0) - bbox = Gtk.ButtonBox() - self.apply_button = Gtk.Button(label=_("Execute ")) - self.apply_button.connect("clicked", self.apply_clicked) - self.apply_button.set_tooltip_text(_("Execute the script")) - css = b"* {background: #00aa00; color: white}" - provider = Gtk.CssProvider() - try: - provider.load_from_data(css) - self.apply_button.get_style_context().add_provider( - provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ) - except: - pass - - bbox.pack_start(self.apply_button, False, False, 6) - widget.pack_start(bbox, False, False, 6) - self.statusmsg = Gtk.Label(_("Ready...")) self.statusmsg.set_xalign(0) # 0.0 for left, 0.5 for center, 1.0 for right self.statusmsg.get_style_context().add_class('bordered-label') #add a css class From 19b283374a3d3a3cf87f698f254c2b24982bc5be Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:19:12 -0700 Subject: [PATCH 003/156] Show the current script's filename in the status area Adds a persistent filename label on the left of the status bar (split from the transient status message via an HBox), updated whenever a script is loaded or saved, so it's always clear which script is loaded. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index b2797eea9..df95a0f8c 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -335,6 +335,7 @@ def init(self): self.gui.WIDGET = self.build_gui() self.gui.get_container_widget().remove(self.gui.textview) self.gui.get_container_widget().add(self.gui.WIDGET) + self.update_filename_label() if os.path.exists(self.last_filename): self.ebuf.set_text(open(self.last_filename).read()) self.statusmsg.set_text("Loaded %r" % self.last_filename) @@ -472,9 +473,6 @@ def build_gui(self): widget.pack_start(self.notebook, True, True, 0) - self.statusmsg = Gtk.Label(_("Ready...")) - self.statusmsg.set_xalign(0) # 0.0 for left, 0.5 for center, 1.0 for right - self.statusmsg.get_style_context().add_class('bordered-label') #add a css class css = b""" .bordered-label { border: 1px solid gray; @@ -483,14 +481,33 @@ def build_gui(self): """ provider = Gtk.CssProvider() provider.load_from_data(css) + + self.filename_label = Gtk.Label() + self.filename_label.set_xalign(0) + self.filename_label.get_style_context().add_class('bordered-label') + self.filename_label.get_style_context().add_provider( + provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + self.statusmsg = Gtk.Label(_("Ready...")) + self.statusmsg.set_xalign(0) # 0.0 for left, 0.5 for center, 1.0 for right + self.statusmsg.get_style_context().add_class('bordered-label') #add a css class self.statusmsg.get_style_context().add_provider( provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) - widget.pack_start(self.statusmsg, False, False, 1) + + status_box = Gtk.HBox() + status_box.pack_start(self.filename_label, False, False, 1) + status_box.pack_start(self.statusmsg, True, True, 1) + widget.pack_start(status_box, False, False, 1) widget.show_all() return widget + def update_filename_label(self): + name = os.path.basename(self.last_filename) if self.last_filename else _("Untitled") + self.filename_label.set_text(name) + def new_script(self, widget): # type: (Any) -> None self.ebuf.set_text("") @@ -517,6 +534,7 @@ def open_script(self, widget): self.last_filename = filename config.set("defaults.last_filename", filename) config.save() + self.update_filename_label() self.statusmsg.set_text("Loaded %r" % self.last_filename) break @@ -554,6 +572,7 @@ def save_as_script(self, widget): self.last_filename = filename config.set("defaults.last_filename", filename) config.save() + self.update_filename_label() self.statusmsg.set_text("Saved as %r (now current)" % self.last_filename) break From 92f34057d0210b961ab8acc4e3797ca5271cdb72 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:23:41 -0700 Subject: [PATCH 004/156] Make the filename label bold and borderless, with more spacing The filename label was using the same bordered style as the status message, making the two indistinguishable. Give it its own bold, borderless style and more room from the status text next to it. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index df95a0f8c..55d1289ed 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -478,13 +478,17 @@ def build_gui(self): border: 1px solid gray; padding: 1px; } + .bold-label { + font-weight: bold; + padding: 1px; + } """ provider = Gtk.CssProvider() provider.load_from_data(css) self.filename_label = Gtk.Label() self.filename_label.set_xalign(0) - self.filename_label.get_style_context().add_class('bordered-label') + self.filename_label.get_style_context().add_class('bold-label') self.filename_label.get_style_context().add_provider( provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) @@ -498,7 +502,7 @@ def build_gui(self): status_box = Gtk.HBox() status_box.pack_start(self.filename_label, False, False, 1) - status_box.pack_start(self.statusmsg, True, True, 1) + status_box.pack_start(self.statusmsg, True, True, 10) widget.pack_start(status_box, False, False, 1) widget.show_all() From 64fc4715356c8a2db1c2deca018349ee2f623412 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:27:04 -0700 Subject: [PATCH 005/156] Prompt to save unsaved changes before New or Open Uses the buffer's built-in modified flag plus Gramps' standard SaveDialog (Save / Don't Save / Cancel) so New and Open no longer silently discard in-progress edits. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 55d1289ed..0769efeec 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -44,7 +44,7 @@ from gramps.gui.widgets.undoablebuffer import UndoableBuffer from gramps.gui.utils import match_primary_mask from gramps.gen.config import config as configman -from gramps.gui.dialog import OkDialog, ErrorDialog +from gramps.gui.dialog import OkDialog, ErrorDialog, SaveDialog from gramps.gui.editors import ( EditCitation, EditEvent, @@ -348,6 +348,7 @@ def init(self): row(person) """ ) + self.ebuf.set_modified(False) def build_gui(self): """ @@ -512,13 +513,49 @@ def update_filename_label(self): name = os.path.basename(self.last_filename) if self.last_filename else _("Untitled") self.filename_label.set_text(name) + def check_unsaved_changes(self, proceed): + """ + If the script has unsaved changes, ask the user whether to save, + discard, or cancel before calling `proceed`. Otherwise call + `proceed` immediately. + """ + if not self.ebuf.get_modified(): + proceed() + return + + def discard(): + proceed() + + def save_then_proceed(): + self.save_script(None) + if not self.ebuf.get_modified(): + proceed() + + SaveDialog( + _("Save Changes?"), + _( + "If you continue without saving, the changes you have " + "made to this script will be lost." + ), + discard, + save_then_proceed, + parent=self.uistate.window, + ) + def new_script(self, widget): # type: (Any) -> None + self.check_unsaved_changes(self._do_new_script) + + def _do_new_script(self): self.ebuf.set_text("") + self.ebuf.set_modified(False) self.statusmsg.set_text("Ready...") def open_script(self, widget): # type: (Gtk.Widget) -> None + self.check_unsaved_changes(self._do_open_script) + + def _do_open_script(self): choose_file_dialog = ScriptOpenFileChooserDialog(self.uistate) if self.last_filename: choose_file_dialog.set_filename(self.last_filename) @@ -534,6 +571,7 @@ def open_script(self, widget): elif response == Gtk.ResponseType.OK: filename = choose_file_dialog.get_filename() self.ebuf.set_text(open(filename).read()) + self.ebuf.set_modified(False) self.statusmsg.set_text("Script loaded") self.last_filename = filename config.set("defaults.last_filename", filename) @@ -550,6 +588,7 @@ def save_script(self, widget): return with open(self.last_filename, "w") as fp: fp.write(self.get_text()) + self.ebuf.set_modified(False) self.statusmsg.set_text("Saved %r" % self.last_filename) def save_as_script(self, widget): @@ -573,6 +612,7 @@ def save_as_script(self, widget): filename = choose_file_dialog.get_filename() with open(filename, "w") as fp: fp.write(self.get_text()) + self.ebuf.set_modified(False) self.last_filename = filename config.set("defaults.last_filename", filename) config.save() From c4cacef503c1652b45609b84c6adc29e82560cfc Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 11:50:30 -0700 Subject: [PATCH 006/156] Add update_script_descriptions.py to keep the examples catalog in sync Moves get_columns()/extract_header_comment() into a new script_utils.py (no GTK/Gramps imports) so both GrampyScript.py and this new dev tool can share them, and so the two tests exercising them no longer need the ast-extraction workaround. update_script_descriptions.py scans scripts/*.gram.py and keeps SCRIPT_DESCRIPTIONS in script_descriptions.py in sync: adds a stub entry for new scripts, drops entries for deleted ones, and warns (without overwriting) when a file's title comment has drifted from the catalogued title. Existing entries' exact source text is preserved via ast slicing. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 35 +--- GrampyScript/script_utils.py | 59 +++++++ .../tests/test_extract_header_comment.py | 31 +--- GrampyScript/tests/test_get_columns.py | 35 +--- GrampyScript/update_script_descriptions.py | 167 ++++++++++++++++++ 5 files changed, 239 insertions(+), 88 deletions(-) create mode 100644 GrampyScript/script_utils.py create mode 100644 GrampyScript/update_script_descriptions.py diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 0769efeec..50714f4da 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -19,7 +19,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import csv -import ast import keyword import datetime from collections import defaultdict @@ -58,6 +57,7 @@ ) from datadict2 import DataDict2, NoneData, set_sa from script_descriptions import SCRIPT_DESCRIPTIONS +from script_utils import get_columns, extract_header_comment, SCRIPTS_DIR _ = glocale.translation.gettext @@ -76,39 +76,6 @@ def contains_any_none_data(args): return not isinstance(args, NoneData) -def get_columns(source, func_name): - try: - tree = ast.parse(source) - for node in ast.walk(tree): - if isinstance(node, ast.Call): - if hasattr(node.func, "id") and node.func.id == func_name: - return [ast.unparse(arg) for arg in node.args] - except Exception: - pass - return [] - - -def extract_header_comment(source): - """ - Extract the leading '#'-comment block of a script as plain text, - for use as a fallback preview when a file has no catalogued - description in SCRIPT_DESCRIPTIONS. - """ - lines = [] - for line in source.splitlines(): - stripped = line.strip() - if stripped.startswith("#"): - lines.append(stripped.lstrip("#").strip()) - elif stripped == "" and not lines: - continue - else: - break - return "\n".join(lines).strip() - - -SCRIPTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "scripts") - - class ScriptOpenFileChooserDialog(Gtk.FileChooserDialog): def __init__(self, uistate): # type: (DisplayState) -> None diff --git a/GrampyScript/script_utils.py b/GrampyScript/script_utils.py new file mode 100644 index 000000000..9ae2032de --- /dev/null +++ b/GrampyScript/script_utils.py @@ -0,0 +1,59 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Pure-Python helpers shared by GrampyScript.py, update_script_descriptions.py, +and the test suite. Kept free of GTK/Gramps imports so they can be used +from a plain script or test without needing a GUI environment. +""" + +import ast +import os + +SCRIPTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "scripts") + + +def get_columns(source, func_name): + try: + tree = ast.parse(source) + for node in ast.walk(tree): + if isinstance(node, ast.Call): + if hasattr(node.func, "id") and node.func.id == func_name: + return [ast.unparse(arg) for arg in node.args] + except Exception: + pass + return [] + + +def extract_header_comment(source): + """ + Extract the leading '#'-comment block of a script as plain text, + for use as a fallback preview when a file has no catalogued + description in SCRIPT_DESCRIPTIONS. + """ + lines = [] + for line in source.splitlines(): + stripped = line.strip() + if stripped.startswith("#"): + lines.append(stripped.lstrip("#").strip()) + elif stripped == "" and not lines: + continue + else: + break + return "\n".join(lines).strip() diff --git a/GrampyScript/tests/test_extract_header_comment.py b/GrampyScript/tests/test_extract_header_comment.py index dd9d2168e..bbcf16998 100644 --- a/GrampyScript/tests/test_extract_header_comment.py +++ b/GrampyScript/tests/test_extract_header_comment.py @@ -1,40 +1,21 @@ """ -Tests for the extract_header_comment() utility from GrampyScript. +Tests for the extract_header_comment() utility in script_utils. extract_header_comment() pulls the leading '#'-comment block out of a script's source, for use as a fallback Open-dialog preview when a file -has no catalogued entry in SCRIPT_DESCRIPTIONS. It is pure ast/string -handling -- no GTK or Gramps imports required -- so, like get_columns, it -is tested here by re-importing it directly from the module source via -ast, without pulling in the full (GTK-dependent) GrampyScript module. +has no catalogued entry in SCRIPT_DESCRIPTIONS. It lives in script_utils.py +(no GTK or Gramps imports required) precisely so it can be imported and +tested directly, without pulling in the full (GTK-dependent) GrampyScript +module. """ -import ast import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -_SOURCE = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "GrampyScript.py" -) - - -def _load_extract_header_comment(): - src = open(_SOURCE).read() - tree = ast.parse(src) - for node in ast.walk(tree): - if isinstance(node, ast.FunctionDef) and node.name == "extract_header_comment": - snippet = ast.unparse(node) - ns = {} - exec(snippet, ns) - return ns["extract_header_comment"] - raise RuntimeError("extract_header_comment not found in GrampyScript.py") - - -extract_header_comment = _load_extract_header_comment() +from script_utils import extract_header_comment class TestExtractHeaderComment(unittest.TestCase): diff --git a/GrampyScript/tests/test_get_columns.py b/GrampyScript/tests/test_get_columns.py index 6270d7b76..b3cdb045b 100644 --- a/GrampyScript/tests/test_get_columns.py +++ b/GrampyScript/tests/test_get_columns.py @@ -1,43 +1,20 @@ """ -Tests for the get_columns() utility from GrampyScript. +Tests for the get_columns() utility in script_utils. get_columns parses Python source and extracts argument expressions from all -calls to a given function name (typically "row"). It is pure ast — no GTK -or Gramps imports required — so the function is tested here by reimporting -it directly from the module source via ast/importlib. +calls to a given function name (typically "row"). It lives in script_utils.py +(no GTK or Gramps imports required) precisely so it can be imported and +tested directly, without pulling in the full (GTK-dependent) GrampyScript +module. """ -import ast import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -# --------------------------------------------------------------------------- -# Load get_columns without importing the full GrampyScript module -# (which needs GTK). We parse the source and exec just the function. -# --------------------------------------------------------------------------- - -_SOURCE = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "GrampyScript.py" -) - - -def _load_get_columns(): - src = open(_SOURCE).read() - tree = ast.parse(src) - for node in ast.walk(tree): - if isinstance(node, ast.FunctionDef) and node.name == "get_columns": - snippet = ast.unparse(node) - ns = {"ast": ast} - exec(snippet, ns) - return ns["get_columns"] - raise RuntimeError("get_columns not found in GrampyScript.py") - - -get_columns = _load_get_columns() +from script_utils import get_columns # --------------------------------------------------------------------------- diff --git a/GrampyScript/update_script_descriptions.py b/GrampyScript/update_script_descriptions.py new file mode 100644 index 000000000..4652e7eec --- /dev/null +++ b/GrampyScript/update_script_descriptions.py @@ -0,0 +1,167 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Dev tool: keep script_descriptions.py's SCRIPT_DESCRIPTIONS dict in sync +with the files actually present in scripts/. + +Run it after adding a new example script, deleting one, or renaming one: + + python3 update_script_descriptions.py + +What it does automatically (safe, structural, nothing to lose): + - Adds a stub entry -- title taken from the new file's leading '#' + comment, description a "TODO" placeholder -- for any scripts/*.gram.py + file with no entry yet. + - Drops entries for files that no longer exist in scripts/. + +What it only *warns* about (needs a human judgment call): + - A file whose leading comment title no longer matches the title + already catalogued in SCRIPT_DESCRIPTIONS. Titles are not + auto-overwritten, since the catalogued one may have been deliberately + written differently (and richer) than the terse in-file comment. + +Existing entries' source text (title + description, translator comments, +line wrapping, quoting) is preserved byte-for-byte by slicing it straight +out of the current file with ast -- this script never touches wording it +didn't generate itself. +""" + +import ast +import glob +import os +import sys + +from script_utils import SCRIPTS_DIR, extract_header_comment + +DESCRIPTIONS_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "script_descriptions.py" +) + +STUB_DESCRIPTION = "TODO: describe what this script does." + + +def _slice_source(lines, node): + start_line, start_col = node.lineno - 1, node.col_offset + end_line, end_col = node.end_lineno - 1, node.end_col_offset + if start_line == end_line: + return lines[start_line][start_col:end_col] + parts = [lines[start_line][start_col:]] + parts.extend(lines[start_line + 1 : end_line]) + parts.append(lines[end_line][:end_col]) + return "".join(parts) + + +def _load_existing(path): + """ + Returns (header, entries) where header is the file text up through + "SCRIPT_DESCRIPTIONS = {" and entries maps filename -> (title, + raw_value_source) using the tuple's exact original source text. + """ + source = open(path, encoding="utf-8").read() + tree = ast.parse(source) + lines = source.splitlines(keepends=True) + + dict_node = None + for node in ast.walk(tree): + if isinstance(node, ast.Assign) and any( + isinstance(t, ast.Name) and t.id == "SCRIPT_DESCRIPTIONS" + for t in node.targets + ): + dict_node = node.value + break + if dict_node is None: + raise RuntimeError("SCRIPT_DESCRIPTIONS not found in %s" % path) + + header = "".join(lines[: dict_node.lineno - 1]) + "SCRIPT_DESCRIPTIONS = {\n" + + entries = {} + for key_node, value_node in zip(dict_node.keys, dict_node.values): + filename = ast.literal_eval(key_node) + title = value_node.elts[0].args[0].value + raw_value = _slice_source(lines, value_node) + entries[filename] = (title, raw_value) + return header, entries + + +def _stub_entry(title): + return '(\n _(%r),\n _(%r),\n )' % (title, STUB_DESCRIPTION) + + +def main(): + header, existing = _load_existing(DESCRIPTIONS_PATH) + + current_files = sorted( + os.path.basename(p) + for p in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")) + ) + + added, removed, retitled_warnings = [], [], [] + + body_lines = [] + for filename in current_files: + file_title = extract_header_comment( + open(os.path.join(SCRIPTS_DIR, filename)).read() + ) + if filename in existing: + title, raw_value = existing[filename] + if file_title and file_title != title: + retitled_warnings.append((filename, title, file_title)) + else: + title, raw_value = file_title or filename, _stub_entry( + file_title or filename + ) + added.append(filename) + body_lines.append(' "%s": %s,\n' % (filename, raw_value)) + + for filename in existing: + if filename not in current_files: + removed.append(filename) + + new_source = header + "".join(body_lines) + "}\n" + + with open(DESCRIPTIONS_PATH, "w", encoding="utf-8") as fp: + fp.write(new_source) + + if added: + print("Added stub entries (fill in real descriptions):") + for filename in added: + print(" + %s" % filename) + if removed: + print("Removed stale entries (file no longer in scripts/):") + for filename in removed: + print(" - %s" % filename) + if retitled_warnings: + print("Title mismatches (file comment changed, catalogued title did not):") + for filename, old_title, new_title in retitled_warnings: + print(" ! %s" % filename) + print(" catalogued: %r" % old_title) + print(" in file: %r" % new_title) + print( + " -> update the title in script_descriptions.py by hand if the " + "file's title is now the correct one." + ) + if not (added or removed or retitled_warnings): + print("script_descriptions.py is already in sync with scripts/.") + + return 1 if retitled_warnings else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 7db27469ecbe36e2a912cd6b17647d5caa1084e9 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 12:42:29 -0700 Subject: [PATCH 007/156] Add import support, custom_filter(), and delete() to GrampyScript Scripts can now import plain .py helper modules placed next to them (scripts dir and the open script's own dir are added to sys.path before exec), reuse an existing Gramps sidebar custom filter via custom_filter(), and remove an object via delete() instead of needing to know the per-class remove_* db call. Also fixes active_event to return a DataDict2 like every other active_* constant, instead of a raw handle. Adds three example scripts (and a script_helpers.py helper module) to scripts/, catalogued in script_descriptions.py and scripts/README.md. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 33 ++++++++++++++++++- GrampyScript/script_descriptions.py | 30 +++++++++++++++++ .../scripts/11_import_example.gram.py | 16 +++++++++ .../scripts/12_custom_filter_example.gram.py | 4 +++ .../13_delete_unused_repositories.gram.py | 12 +++++++ GrampyScript/scripts/README.md | 8 +++++ GrampyScript/scripts/script_helpers.py | 9 +++++ 7 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 GrampyScript/scripts/11_import_example.gram.py create mode 100644 GrampyScript/scripts/12_custom_filter_example.gram.py create mode 100644 GrampyScript/scripts/13_delete_unused_repositories.gram.py create mode 100644 GrampyScript/scripts/script_helpers.py diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 50714f4da..37e98fa8f 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -33,6 +33,7 @@ from gi.repository import Gtk, Gdk, cairo, Pango from gramps.gen.db import DbTxn +from gramps.gen import filters as gramps_filters from gramps.gen.plug import Gramplet from gramps.gen.display.name import displayer as name_displayer from gramps.gen.display.place import displayer as place_displayer @@ -275,6 +276,8 @@ def init(self): "events", "selected", "filtered", + "custom_filter", + "delete", ] self.constants = [ "True", @@ -1111,6 +1114,15 @@ def evaluate_expression(self, code): """Run code in the full GrampyScript scope and return stdout.""" return self.execute_code(code) + def ensure_import_paths(self): + """Make helper .py files next to scripts importable via `import`.""" + paths = [SCRIPTS_DIR] + if self.last_filename: + paths.append(os.path.dirname(os.path.abspath(self.last_filename))) + for path in paths: + if path and path not in sys.path: + sys.path.insert(0, path) + def execute_filename(self, filename): if os.path.exists(filename): with open(filename) as file: @@ -1196,7 +1208,7 @@ def columns(*column_names): active_source = self.get_active_data("Source") active_citation = self.get_active_data("Citation") active_place = self.get_active_data("Place") - active_event = self.get_active("Event") + active_event = self.get_active_data("Event") chart = self.chart @@ -1226,6 +1238,24 @@ def filtered(table_name): data = get_data(handle) yield DataDict2(dict(data), callback=self.callback) + def custom_filter(name, namespace="Person"): + if gramps_filters.CustomFilters is None: + gramps_filters.reload_custom_filters() + filt = gramps_filters.CustomFilters.get_filters_dict(namespace).get(name) + if filt is None: + print( + "Warning: no custom filter named %r for namespace %r" + % (name, namespace) + ) + return + get_data = self.db._get_table_func(namespace, "raw_func") + for handle in filt.apply(self.db): + yield DataDict2(dict(get_data(handle)), callback=self.callback) + + def delete(obj): + del_func = self.db._get_table_func(obj["_class"], "del_func") + del_func(obj["handle"], self.TRANSACTION) + database = self.db today = Date( @@ -1243,6 +1273,7 @@ def filtered(table_name): self.TRANSACTION = None self.output_buffer.set_text("") + self.ensure_import_paths() # ----------------- # User code # FIXME: don't use stdout? diff --git a/GrampyScript/script_descriptions.py b/GrampyScript/script_descriptions.py index fbbdccef8..9c43e50f3 100644 --- a/GrampyScript/script_descriptions.py +++ b/GrampyScript/script_descriptions.py @@ -110,4 +110,34 @@ "records." ), ), + "11_import_example.gram.py": ( + _("Births Per Decade (Import Example)"), + _( + "Counts births by decade, using a decade() function imported " + "from script_helpers.py in this same folder — a template for " + "sharing helper code between your own scripts with a plain " + "'import' statement." + ), + ), + "12_custom_filter_example.gram.py": ( + _("Custom Filter Example"), + _( + "Runs one of your own custom filters (from the Filters " + "gramplet/editor) by name using custom_filter(). Change " + "'example filter' to the name of a filter you've already " + "created; if the name doesn't match one, a warning shows up " + "in the Output tab instead." + ), + ), + "13_delete_unused_repositories.gram.py": ( + _("Delete Unused Repositories (Delete Example)"), + _( + "Demonstrates delete(): removes any Repository record that " + "nothing else in the tree refers to. Most trees have no " + "unused repositories, so this is unlikely to actually delete " + "anything — it's meant to show the pattern, wrapped in " + "begin_changes()/end_changes() as a single undoable " + "transaction." + ), + ), } diff --git a/GrampyScript/scripts/11_import_example.gram.py b/GrampyScript/scripts/11_import_example.gram.py new file mode 100644 index 000000000..8b0b97c8c --- /dev/null +++ b/GrampyScript/scripts/11_import_example.gram.py @@ -0,0 +1,16 @@ +# Births Per Decade (Import Example) + +from script_helpers import decade + +columns("Decade", "Births") + +counts = counter() +for person in people(): + birth = person.birth + if birth: + year = birth.get_date_object().get_year() + if year: + counts[decade(year)] += 1 + +for decade_start, count in sorted(counts.items()): + row("%ds" % decade_start, count) diff --git a/GrampyScript/scripts/12_custom_filter_example.gram.py b/GrampyScript/scripts/12_custom_filter_example.gram.py new file mode 100644 index 000000000..da212b84b --- /dev/null +++ b/GrampyScript/scripts/12_custom_filter_example.gram.py @@ -0,0 +1,4 @@ +# Custom Filter Example + +for person in custom_filter("example filter"): + row(person) diff --git a/GrampyScript/scripts/13_delete_unused_repositories.gram.py b/GrampyScript/scripts/13_delete_unused_repositories.gram.py new file mode 100644 index 000000000..d9e657efc --- /dev/null +++ b/GrampyScript/scripts/13_delete_unused_repositories.gram.py @@ -0,0 +1,12 @@ +# Delete Unused Repositories (Delete Example) + +begin_changes("Delete unused repositories") + +count = 0 +for repository in repositories(): + if not repository.back_references: + delete(repository) + count += 1 + +end_changes() +print("Deleted %d unused repositories" % count) diff --git a/GrampyScript/scripts/README.md b/GrampyScript/scripts/README.md index 294c1d2f4..47aab2acb 100644 --- a/GrampyScript/scripts/README.md +++ b/GrampyScript/scripts/README.md @@ -16,10 +16,18 @@ numbered files below are examples; anything else you save here is yours. | `08_active_person_summary.gram.py` | Summary of the active person plus their parents, spouse, and children. | | `09_selected_people_report.gram.py` | Report on just the rows currently selected in the People view. | | `10_find_missing_birth_dates.gram.py` | Data-quality check: people with no recorded birth event. | +| `11_import_example.gram.py` | Counts births per decade using `decade()`, imported from `script_helpers.py`. | +| `12_custom_filter_example.gram.py` | Runs one of your own custom filters by name via `custom_filter()`. | +| `13_delete_unused_repositories.gram.py` | Delete example: removes Repository records nothing refers to (rarely any). | Each script's title and description are also shown as a preview when you highlight it in the Open dialog. +`script_helpers.py` is a plain Python module, not a `.gram.py` script — it +won't show up in the Open dialog. It exists to be imported (see +`11_import_example.gram.py`): any `.py` file you place in this folder, or +alongside a script saved elsewhere, can be imported the same way. + **Note:** addon updates only overwrite files that share a name with something in the released package. It's safe to save your own scripts in this folder under any other name — just avoid editing the numbered diff --git a/GrampyScript/scripts/script_helpers.py b/GrampyScript/scripts/script_helpers.py new file mode 100644 index 000000000..482fd3454 --- /dev/null +++ b/GrampyScript/scripts/script_helpers.py @@ -0,0 +1,9 @@ +# Plain Python helper module, importable from any .gram.py script in this +# folder via `from script_helpers import decade` (see 11_import_example.gram.py). +# Unlike .gram.py files this is not a runnable script itself -- it's a +# regular module that GrampyScript's import-path setup makes importable. + + +def decade(year): + """Round a year down to the start of its decade, e.g. 1873 -> 1870.""" + return (year // 10) * 10 if year else None From 434f7685ddc47612172066c397027355a9e6b608 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 15:09:24 -0700 Subject: [PATCH 008/156] Add jedi-based Tab completion to the GrampyScript editor Wires a Tab-triggered, live-filtering completion popover into the script editor, covering plain Python names, the DSL's own functions (people(), custom_filter(), selected()/filtered(), ...), and nested attribute chains on Gramps records (person.primary_name.first_name), including through a user's own loop variables and list subscripts. completion.py wraps jedi.Interpreter for the actual lookups. stub_generator.py derives static type stubs straight from Gramps' own get_schema() so jedi can infer generator/loop-variable row types without ever executing anything (a live template object would require calling DataDict2's computed properties, e.g. father/birth, which only degrade to empty results for blank data anyway). namespace_builder.py supplies the remaining live objects (today, counter, database) that are safe to introspect directly. completion_popup.py is a standalone Gtk.Popover controller, kept independent of the Gramplet class so it's testable against a plain Gtk.TextView. DataDict2 gained a __dir__ override so introspection (jedi's runtime fallback) sees dynamic dict keys like primary_name, not just its declared properties. Also fixes the editor ScrolledWindow/TextView having no wrap mode or explicit scroll policy, which let long lines widen the whole gramplet instead of scrolling within it. Requires jedi (added to GrampyScript.gpr.py's requires_mod), which ships with Gramps 6.1. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.gpr.py | 1 + GrampyScript/GrampyScript.py | 17 ++ GrampyScript/completion.py | 92 ++++++ GrampyScript/completion_popup.py | 296 +++++++++++++++++++ GrampyScript/datadict2.py | 6 + GrampyScript/namespace_builder.py | 59 ++++ GrampyScript/stub_generator.py | 240 +++++++++++++++ GrampyScript/tests/test_completion.py | 145 +++++++++ GrampyScript/tests/test_completion_popup.py | 201 +++++++++++++ GrampyScript/tests/test_namespace_builder.py | 46 +++ GrampyScript/tests/test_stub_generator.py | 165 +++++++++++ 11 files changed, 1268 insertions(+) create mode 100644 GrampyScript/completion.py create mode 100644 GrampyScript/completion_popup.py create mode 100644 GrampyScript/namespace_builder.py create mode 100644 GrampyScript/stub_generator.py create mode 100644 GrampyScript/tests/test_completion.py create mode 100644 GrampyScript/tests/test_completion_popup.py create mode 100644 GrampyScript/tests/test_namespace_builder.py create mode 100644 GrampyScript/tests/test_stub_generator.py diff --git a/GrampyScript/GrampyScript.gpr.py b/GrampyScript/GrampyScript.gpr.py index 20258e0dc..55ddf10a8 100644 --- a/GrampyScript/GrampyScript.gpr.py +++ b/GrampyScript/GrampyScript.gpr.py @@ -32,4 +32,5 @@ gramplet_title=_("Gram.py Script"), help_url="Addon:GrampyScript", height=800, + requires_mod=["jedi"], ) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 37e98fa8f..377fb959d 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -59,6 +59,8 @@ from datadict2 import DataDict2, NoneData, set_sa from script_descriptions import SCRIPT_DESCRIPTIONS from script_utils import get_columns, extract_header_comment, SCRIPTS_DIR +from namespace_builder import build_namespace +from completion_popup import CompletionController _ = glocale.translation.gettext @@ -367,7 +369,9 @@ def build_gui(self): self.editor = Gtk.ScrolledWindow() self.editor.set_shadow_type(Gtk.ShadowType.IN) + self.editor.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.editor_textview = Gtk.TextView() + self.editor_textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) self.editor.add(self.editor_textview) font_desc = self.editor_textview.get_pango_context().get_font_description() font_desc.set_family( @@ -377,6 +381,7 @@ def build_gui(self): self.editor_textview.connect("key-press-event", self.on_key_press) self.editor_textview.connect("button-press-event", self.on_textview_click) + self.editor_textview.connect("focus-out-event", self.on_editor_focus_out) key, mods = Gtk.accelerator_parse("c") self.editor_textview.add_accelerator( "copy-clipboard", self.accel_group, key, mods, Gtk.AccelFlags.VISIBLE @@ -404,6 +409,9 @@ def build_gui(self): "comment", foreground="gray", style=Pango.Style.ITALIC ) self.ebuf.connect("changed", self.on_buffer_changed) + self.completion = CompletionController( + self.editor_textview, get_namespace=lambda: build_namespace(self.dbstate.db) + ) widget.pack_start(self.editor, True, True, 0) @@ -663,6 +671,7 @@ def copy_to_clipboard(self, widget): def on_buffer_changed(self, buffer): self.highlight_syntax() + self.completion.on_buffer_changed() def highlight_syntax(self): start_iter = self.ebuf.get_start_iter() @@ -876,10 +885,18 @@ def pp(self, item): return str(item) def on_textview_click(self, widget, event): + self.completion.close() if event.button == 1: # Left mouse button widget.grab_focus() + def on_editor_focus_out(self, widget, event): + self.completion.close() + return False + def on_key_press(self, textview, event): + if self.completion.on_key_press(event): + return True + if event.keyval == Gdk.KEY_Tab: # buffer = textview.get_buffer() iter_ = self.ebuf.get_iter_at_mark(self.ebuf.get_insert()) diff --git a/GrampyScript/completion.py b/GrampyScript/completion.py new file mode 100644 index 000000000..76950d945 --- /dev/null +++ b/GrampyScript/completion.py @@ -0,0 +1,92 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Command completion for the GrampyScript editor, built on jedi. + +Kept free of GTK imports so it can be developed and tested without a +running Gramps/GTK environment; GrampyScript.py is responsible for +turning a Gtk.TextBuffer cursor position into (line, column) and for +building the namespace of live/template DSL objects. +""" + +import jedi + +from stub_generator import build_stub_source + +_stub_source = None + + +def _get_stub_preamble(): + """ + Lazily build and cache the stub-class source (stub_generator.py): + static type annotations, derived from Gramps' own get_schema(), that + let jedi infer the row type of DSL generators like `people()` for a + user's own loop variable -- something no live namespace object can + provide, since there is no instance until the script actually runs. + """ + global _stub_source + if _stub_source is None: + _stub_source = build_stub_source() + return _stub_source + + +def _complete(source, line, column, namespace): + """Shared jedi call underlying both get_completions() and + get_completion_items(); returns raw jedi Completion objects.""" + preamble = _get_stub_preamble() + full_source = preamble + source + interpreter = jedi.Interpreter(full_source, [namespace]) + try: + return interpreter.complete(line + preamble.count("\n"), column) + except Exception: + return [] + + +def get_completions(source, line, column, namespace): + """ + Return candidate completion names for `source` at the given cursor + position. `line`/`column` refer to `source` itself (1-indexed / + 0-indexed, jedi's convention, matching Gtk.TextIter's + get_line()+1 / get_line_offset()); the stub preamble prepended below + is accounted for internally. + + `namespace` is a plain dict of name -> live or template object, + e.g. {"active_person": DataDict2(...), "database": self.db}. + Attribute completion on dynamic objects (like DataDict2) relies on + those objects implementing __dir__ correctly, since jedi falls back + to runtime introspection (dir()/getattr()) for anything it can't + statically analyze. + """ + return [completion.name for completion in _complete(source, line, column, namespace)] + + +def get_completion_items(source, line, column, namespace): + """ + Same as get_completions(), but for UI use: returns a list of + {"name": full completion name, "complete": text to insert at the + cursor} dicts. `name` is for display; `complete` is only the + remaining characters jedi says are missing (e.g. typing "impo" and + accepting "import" gives complete == "rt"), so callers can insert it + directly without recomputing/re-typing the already-typed prefix. + """ + return [ + {"name": completion.name, "complete": completion.complete} + for completion in _complete(source, line, column, namespace) + ] diff --git a/GrampyScript/completion_popup.py b/GrampyScript/completion_popup.py new file mode 100644 index 000000000..21a7e4546 --- /dev/null +++ b/GrampyScript/completion_popup.py @@ -0,0 +1,296 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +A Tab-triggered, live-filtering completion popover for a Gtk.TextView, +built on completion.get_completion_items(). + +Kept as a standalone controller (not part of GrampyScript.py's Gramplet +class) so it can be driven directly against a plain Gtk.TextView in +tests, independent of the full Gramps Gramplet machinery. + +Wiring it into a host widget requires forwarding four things: + textview "key-press-event" -> controller.on_key_press(event) + (if it returns True, treat the event + as handled and stop further processing) + buffer "changed" -> controller.on_buffer_changed() + textview "button-press-event"/"focus-out-event" -> controller.close() +""" + +import logging + +from gi.repository import Gdk, Gtk + +from completion import get_completion_items + +_LOG = logging.getLogger(".GrampyScript.completion") + +_NAVIGATION_KEYS = ( + Gdk.KEY_Left, + Gdk.KEY_Right, + Gdk.KEY_Home, + Gdk.KEY_End, + Gdk.KEY_Page_Up, + Gdk.KEY_Page_Down, +) + + +class CompletionController: + def __init__(self, textview, get_namespace): + """ + `textview`: the Gtk.TextView to attach completion to. + `get_namespace`: zero-arg callable returning the current + namespace dict for get_completion_items() (e.g. + `lambda: build_namespace(self.dbstate.db)`); called fresh on + every request so it always reflects the live database. + """ + self.textview = textview + self.buffer = textview.get_buffer() + self.get_namespace = get_namespace + self.popover = None + self.listbox = None + self.scrolled = None + self.items = [] + self.selected_index = 0 + + # ---- public event entry points ----------------------------------- + + def on_key_press(self, event): + """Return True if the event was consumed and should not be + processed any further by the caller.""" + try: + return self._on_key_press(event) + except Exception: + # Never let a bug here swallow the keypress entirely -- that + # would leave GTK's own default handler to run instead (e.g. + # inserting a literal tab character for Gdk.KEY_Tab), which + # looks like "completion silently does nothing." Log and + # fall back to "not handled" instead. + _LOG.exception("completion on_key_press failed") + self.close() + return False + + def _on_key_press(self, event): + keyval = event.keyval + _LOG.debug("on_key_press keyval=%s open=%s", Gdk.keyval_name(keyval), self.is_open()) + if keyval == Gdk.KEY_Tab: + if self.is_open(): + self.accept() + return True + return self.trigger() + if self.is_open(): + if keyval == Gdk.KEY_Up: + self.move_selection(-1) + return True + if keyval == Gdk.KEY_Down: + self.move_selection(1) + return True + if keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): + self.accept() + return True + if keyval == Gdk.KEY_Escape: + self.close() + return True + if keyval in _NAVIGATION_KEYS: + # The cursor is about to move out from under the popover; + # let it move normally, just stop completing at this spot. + self.close() + return False + return False + + def on_buffer_changed(self): + if not self.is_open(): + return + try: + self.refresh() + except Exception: + _LOG.exception("completion refresh failed") + self.close() + + def is_open(self): + return self.popover is not None + + # ---- core ----------------------------------------------------------- + + def _cursor_iter(self): + return self.buffer.get_iter_at_mark(self.buffer.get_insert()) + + def _cursor_line_column(self): + it = self._cursor_iter() + return it.get_line() + 1, it.get_line_offset() + + def _word_prefix(self): + it = self._cursor_iter() + start = it.copy() + while start.backward_char(): + ch = start.get_char() + if ch.isalnum() or ch == "_": + continue + start.forward_char() + break + return self.buffer.get_text(start, it, True) + + def _is_completable_context(self): + it = self._cursor_iter() + start = it.copy() + if not start.backward_char(): + _LOG.debug("not completable: at start of buffer") + return False + ch = start.get_char() + completable = ch.isalnum() or ch in "_.]" + _LOG.debug("preceding char=%r completable=%s", ch, completable) + return completable + + def _compute_items(self): + source = self.buffer.get_text( + self.buffer.get_start_iter(), self.buffer.get_end_iter(), True + ) + line, column = self._cursor_line_column() + prefix = self._word_prefix() + _LOG.debug("computing completions at line=%s column=%s prefix=%r", line, column, prefix) + try: + namespace = self.get_namespace() + items = get_completion_items(source, line, column, namespace) + except Exception: + _LOG.exception("building completion namespace/items failed") + return [] + if not prefix.startswith("_"): + items = [item for item in items if not item["name"].startswith("_")] + _LOG.debug("found %d completion(s): %s", len(items), [i["name"] for i in items[:10]]) + if not items: + _LOG.debug("zero completions, full source was:\n%s", source) + return items + + def trigger(self): + """Try to open the popover at the cursor. Returns True if it + did (there was something completable to show).""" + if not self._is_completable_context(): + return False + items = self._compute_items() + if not items: + _LOG.debug("trigger: no completions, falling back to default Tab behavior") + return False + self.items = items + self.selected_index = 0 + self._open_popover() + return True + + def refresh(self): + """Recompute matches for an already-open popover, following the + cursor as the user keeps typing. Closes if nothing matches + anymore.""" + if not self._is_completable_context(): + self.close() + return + items = self._compute_items() + if not items: + self.close() + return + self.items = items + self.selected_index = min(self.selected_index, len(items) - 1) + self._rebuild_listbox() + self._reposition() + + def move_selection(self, delta): + if not self.items: + return + self.selected_index = max(0, min(len(self.items) - 1, self.selected_index + delta)) + self._update_row_selection() + + def accept(self): + if self.items: + item = self.items[self.selected_index] + self.buffer.insert(self._cursor_iter(), item["complete"]) + self.close() + + def close(self): + if self.popover is not None: + self.popover.destroy() + self.popover = None + self.listbox = None + self.scrolled = None + self.items = [] + self.selected_index = 0 + + # ---- widget building -------------------------------------------------- + + def _open_popover(self): + self.popover = Gtk.Popover() + self.popover.set_relative_to(self.textview) + self.popover.set_modal(False) + self.popover.set_position(Gtk.PositionType.BOTTOM) + + self.scrolled = Gtk.ScrolledWindow() + self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + self.scrolled.set_max_content_height(200) + self.scrolled.set_propagate_natural_height(True) + + self.listbox = Gtk.ListBox() + self.listbox.set_activate_on_single_click(True) + self.listbox.connect("row-activated", self._on_row_activated) + self.scrolled.add(self.listbox) + self.popover.add(self.scrolled) + + self._rebuild_listbox() + self.popover.show_all() + self._reposition() + self.popover.popup() + + def _rebuild_listbox(self): + for child in self.listbox.get_children(): + self.listbox.remove(child) + for item in self.items: + label = Gtk.Label(label=item["name"], xalign=0) + label.set_margin_start(6) + label.set_margin_end(6) + row = Gtk.ListBoxRow() + row.add(label) + self.listbox.add(row) + self.listbox.show_all() + self._update_row_selection() + + def _update_row_selection(self): + row = self.listbox.get_row_at_index(self.selected_index) + if row is not None: + self.listbox.select_row(row) + self._scroll_to_row(row) + + def _scroll_to_row(self, row): + alloc = row.get_allocation() + adj = self.scrolled.get_vadjustment() + if alloc.y < adj.get_value(): + adj.set_value(alloc.y) + elif alloc.y + alloc.height > adj.get_value() + adj.get_page_size(): + adj.set_value(alloc.y + alloc.height - adj.get_page_size()) + + def _reposition(self): + rect = self.textview.get_iter_location(self._cursor_iter()) + x, y = self.textview.buffer_to_window_coords( + Gtk.TextWindowType.WIDGET, rect.x, rect.y + ) + pointing = Gdk.Rectangle() + pointing.x = x + pointing.y = y + pointing.width = 1 + pointing.height = rect.height + self.popover.set_pointing_to(pointing) + + def _on_row_activated(self, listbox, row): + self.selected_index = row.get_index() + self.accept() diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index 37df19c3a..a969311f5 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -334,6 +334,12 @@ def __setattr__(self, attr, value): # def __str__(self): # return str(self._object) + def __dir__(self): + # Merge real class attributes with the dynamic dict keys, so that + # introspection tools (e.g. jedi-based completion) can see fields + # like `primary_name` that only exist via __getattr__. + return sorted(set(super().__dir__()) | set(self.keys())) + def __getattr__(self, key): if key == "_object": if "_object" not in self: diff --git a/GrampyScript/namespace_builder.py b/GrampyScript/namespace_builder.py new file mode 100644 index 000000000..9bf9dec2b --- /dev/null +++ b/GrampyScript/namespace_builder.py @@ -0,0 +1,59 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Builds the runtime completion namespace: real live objects for the DSL +names whose fields are safe and useful to introspect directly. + +active_person/active_family/etc. are deliberately NOT included here -- +see stub_generator.ACTIVE_VARIABLES. They're handled as static +annotations instead, because completing through them would otherwise +require jedi to actually call DataDict2's computed @property methods +(father, birth, ...) to see what they return, executing real +SimpleAccess lookups for no benefit (a blank template has nothing to +find anyway). + +Kept free of GTK imports so it can be developed and tested without a +running Gramps/GTK environment. +""" + +import datetime +from collections import defaultdict + +from gramps.gen.lib import Date + + +def build_namespace(database=None): + """ + Return a namespace dict for get_completions(), covering the + directly-bound DSL names in execute_code() that are safe to + introspect as live objects: today, counter, and database. + + `database` is the real Gramps database (self.dbstate.db). Passing it + enables completion of its real methods (database.get_person_from_handle, + ...); it's optional since dir() on it never executes anything. + """ + today = datetime.date.today() + namespace = { + "today": Date(today.year, today.month, today.day), + "counter": lambda: defaultdict(int), + } + if database is not None: + namespace["database"] = database + return namespace diff --git a/GrampyScript/stub_generator.py b/GrampyScript/stub_generator.py new file mode 100644 index 000000000..a054ea2c6 --- /dev/null +++ b/GrampyScript/stub_generator.py @@ -0,0 +1,240 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Generates a block of plain-annotation Python source (a "stub preamble") that +jedi can use to statically infer the row type of a DSL generator function, +e.g. `for person in people(): person.primary_name.first_name`. + +jedi's static engine reads class bodies, it never runs our DataDict2.__dir__ +override, so a live DataDict2 instance is not enough for this case (there is +no instance until the script actually runs). Instead we derive the field +names straight from Gramps' own JSON schema (cls.get_schema(), present on +every gramps.gen.lib class) and render lightweight classes carrying only +type annotations -- no bodies, nothing is ever executed. + +This module has no GTK dependency, only gramps.gen.lib, so it can be +developed and tested without a running Gramps/GTK environment. +""" + +import re + +from gramps.gen.lib import ( + Citation, + Event, + Family, + Media, + Note, + Person, + Place, + Repository, + Source, +) + +# The DSL row types generators/active_* names are bound to in +# GrampyScript.execute_code(). +ROOT_CLASSES = [Person, Family, Event, Place, Repository, Source, Citation, Note, Media] + +# generator function name -> row type, mirrors the DSL bindings in +# GrampyScript.execute_code() (people/families/notes/...). +GENERATOR_ROW_TYPES = { + "people": "Person", + "families": "Family", + "notes": "Note", + "events": "Event", + "repositories": "Repository", + "citations": "Citation", + "sources": "Source", + "media": "Media", + "places": "Place", +} + +# active_* name -> row type, mirrors the active_* bindings in +# GrampyScript.execute_code(). These are declared as plain module-level +# annotations (no value), which is enough for jedi to resolve attribute +# chains statically. That matters here: a *live* template DataDict2 +# instance would require jedi to actually call DataDict2's computed +# @property methods (father, birth, ...) to see what they return, which +# executes real code (SimpleAccess lookups) and, for a blank template +# with nothing to find, yields no completions at all past that point. +# The stub sidesteps both problems -- nothing is ever executed, and the +# field list comes from the schema regardless of what data exists. +ACTIVE_VARIABLES = { + "active_person": "Person", + "active_family": "Family", + "active_event": "Event", + "active_place": "Place", + "active_repository": "Repository", + "active_source": "Source", + "active_citation": "Citation", + "active_note": "Note", + "active_media": "Media", +} + +# selected()/filtered()/custom_filter() in execute_code() all take a +# table-name string argument that picks the row type at runtime (e.g. +# selected("Family")). jedi 0.19 does not discriminate typing.overload by +# a Literal argument value -- verified empirically it merges every +# overload's return fields regardless of which literal was passed, and +# regardless of overload declaration order. So rather than rely on that, +# these are typed as returning the union of every row type: less precise +# than the argument deserves, but strictly more useful than no annotation +# at all (which is what these had before). +TABLE_FUNCTIONS = { + "selected": ["table_name: str"], + "filtered": ["table_name: str"], + "custom_filter": ["name: str", 'namespace: str = "Person"'], +} + +# DataDict2's computed @property names (datadict2.py), layered onto every +# generated type since DataDict2 defines them once for every instance +# regardless of the wrapped record's real class. Best-effort types; "object" +# is used where the real return type is ambiguous or data-dependent. +COMPUTED_PROPERTIES = { + "gender": "str", + "age": "object", + "birth": "Event", + "death": "Event", + "place": "Place", + "parents": 'list["Person"]', + "father": "Person", + "mother": "Person", + "spouse": "Person", + "source": "Source", + "families": 'list["Family"]', + "parent_families": 'list["Family"]', + "children": 'list["Person"]', + "notes": 'list["Note"]', + "tags": 'list["Tag"]', + "citations": 'list["Citation"]', + "media": 'list["MediaRef"]', + "events": 'list["Event"]', + "reference": "Person", + "attributes": 'list["Attribute"]', + "addresses": 'list["Address"]', + "lds_ords": 'list["LdsOrdinance"]', + "references": 'list["PersonRef"]', + "back_references": "object", + "back_references_recursively": "object", + "name": "Name", + "surname": "Surname", + "names": 'list["Name"]', +} + +_SCALAR_TYPES = {"string": "str", "integer": "int", "boolean": "bool", "number": "float"} + + +def _sanitize(title): + """Turn a Gramps schema title like "Event reference" into a valid + Python identifier like "EventReference".""" + return "".join(word.capitalize() for word in re.findall(r"[A-Za-z0-9]+", title)) + + +def _pytype(schema, registry): + type_ = schema.get("type") + if isinstance(type_, list): + return "object" + if type_ == "object" and "properties" in schema: + _walk(schema, registry) + return _sanitize(schema["title"]) + if type_ == "array": + items = schema.get("items") + if isinstance(items, dict) and items.get("type") == "object" and "properties" in items: + _walk(items, registry) + return 'list["%s"]' % _sanitize(items["title"]) + return "list" + return _SCALAR_TYPES.get(type_, "object") + + +def _walk(schema, registry): + title = schema.get("title") + if not title: + return + name = _sanitize(title) + if name in registry: + return + registry[name] = {} # reserve first, to break reference cycles + fields = {} + for field_name, sub in schema.get("properties", {}).items(): + if field_name == "_class": + continue + fields[field_name] = _pytype(sub, registry) + registry[name] = fields + + +def build_registry(root_classes=ROOT_CLASSES): + """ + Return {sanitized_class_name: {field_name: type_annotation}} for every + type reachable from `root_classes` via Gramps' own get_schema(), with + DataDict2's computed properties layered on top of each (matching real + attribute lookup order: properties shadow raw dict keys). + """ + registry = {} + for cls in root_classes: + _walk(cls.get_schema(), registry) + for fields in registry.values(): + fields.update(COMPUTED_PROPERTIES) + return registry + + +def render_stub_source( + registry, + generator_row_types=GENERATOR_ROW_TYPES, + table_functions=TABLE_FUNCTIONS, + active_variables=ACTIVE_VARIABLES, +): + """ + Render `registry` plus DSL generator function signatures and active_* + variable annotations as a block of Python source usable as a jedi + completion preamble. No class or function body has real logic, only + annotations -- this text is only ever fed to jedi for static + analysis, never executed. + """ + lines = [ + "from __future__ import annotations", + "from typing import Iterator, Union", + "", + ] + for name in sorted(registry): + lines.append("class %s:" % name) + fields = registry[name] + if not fields: + lines.append(" pass") + else: + for field_name, type_ in fields.items(): + lines.append(" %s: %s" % (field_name, type_)) + lines.append("") + for func_name, row_type in generator_row_types.items(): + lines.append("def %s() -> Iterator[%s]: ..." % (func_name, row_type)) + if table_functions: + row_union = "Union[%s]" % ", ".join(sorted(set(generator_row_types.values()))) + for func_name, params in table_functions.items(): + lines.append( + "def %s(%s) -> Iterator[%s]: ..." % (func_name, ", ".join(params), row_union) + ) + lines.append("") + for var_name, row_type in active_variables.items(): + lines.append("%s: %s" % (var_name, row_type)) + lines.append("") + return "\n".join(lines) + + +def build_stub_source(): + """Convenience: build the registry and render it in one call.""" + return render_stub_source(build_registry()) diff --git a/GrampyScript/tests/test_completion.py b/GrampyScript/tests/test_completion.py new file mode 100644 index 000000000..70c43a5fb --- /dev/null +++ b/GrampyScript/tests/test_completion.py @@ -0,0 +1,145 @@ +""" +Tests for completion.py — jedi-based command completion. + +Uses real Gramps gen-lib objects (no GTK required). +""" + +import os +import sys +import unittest +from unittest.mock import MagicMock + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from gramps.gen.lib import Person, Name, Surname +from gramps.gen.simple import SimpleAccess + +from datadict2 import DataDict2, set_sa +from completion import get_completions, get_completion_items + + +def _make_person(gramps_id="I0001", first="John", surname="Smith"): + p = Person() + p.set_gramps_id(gramps_id) + n = Name() + sn = Surname() + sn.set_surname(surname) + n.add_surname(sn) + n.set_first_name(first) + p.set_primary_name(n) + return p + + +class _MockSaBase(unittest.TestCase): + """Base class that sets up a minimal SimpleAccess mock before each test.""" + + def setUp(self): + db = MagicMock() + sa = SimpleAccess(db) + set_sa(sa) + + def _complete(self, source, namespace): + line = source.count("\n") + 1 + column = len(source) - (source.rfind("\n") + 1) + return get_completions(source, line, column, namespace) + + +class TestBareWordCompletion(_MockSaBase): + def test_completes_python_builtins(self): + names = self._complete("pri", {}) + self.assertIn("print", names) + + def test_completes_namespace_variable(self): + names = self._complete("active_per", {"active_person": DataDict2(_make_person())}) + self.assertIn("active_person", names) + + +class TestAttributeCompletion(_MockSaBase): + def setUp(self): + super().setUp() + self.namespace = {"active_person": DataDict2(_make_person())} + + def test_completes_dynamic_dict_keys(self): + names = self._complete("active_person.", self.namespace) + self.assertIn("primary_name", names) + self.assertIn("gramps_id", names) + + def test_completes_class_properties(self): + names = self._complete("active_person.", self.namespace) + self.assertIn("father", names) + self.assertIn("age", names) + + def test_completes_nested_attribute_chain(self): + names = self._complete("active_person.primary_name.", self.namespace) + self.assertIn("first_name", names) + self.assertIn("surname_list", names) + + def test_prefix_narrows_nested_match(self): + names = self._complete("active_person.primary_name.first_", self.namespace) + self.assertEqual(names, ["first_name"]) + + def test_no_false_match_for_unrelated_prefix(self): + names = self._complete("active_person.primary_name.zzz", self.namespace) + self.assertEqual(names, []) + + +class TestGeneratorRowTypeInference(_MockSaBase): + """ + Completion on a user's own loop variable, e.g. + `for person in people(): person.primary_name.first_name` -- `person` is + a name the user chose, not something we bind into the namespace, so it + can only be resolved via the stub_generator preamble's static + annotation on `people()`, not runtime introspection. + """ + + def test_completes_loop_variable_over_people(self): + names = self._complete("for person in people():\n person.", {}) + self.assertIn("primary_name", names) + self.assertIn("gramps_id", names) + + def test_completes_nested_attribute_on_loop_variable(self): + names = self._complete( + "for person in people():\n person.primary_name.first_", {} + ) + self.assertEqual(names, ["first_name"]) + + def test_distinguishes_row_type_by_generator(self): + # families() yields Family, not Person -- fields must not bleed + # across generators. + names = self._complete("for fam in families():\n fam.", {}) + self.assertIn("father_handle", names) + self.assertNotIn("primary_name", names) + + +class TestCompletionItems(_MockSaBase): + """get_completion_items() is get_completions() plus the jedi + `.complete` suffix, used by the editor to insert just the missing + characters rather than re-typing the whole name.""" + + def test_complete_is_only_the_missing_suffix(self): + namespace = {"active_person": DataDict2(_make_person())} + items = get_completion_items("active_person.primary_", 1, len("active_person.primary_"), namespace) + self.assertEqual(items, [{"name": "primary_name", "complete": "name"}]) + + def test_complete_is_full_name_when_nothing_typed_yet(self): + namespace = {"active_person": DataDict2(_make_person())} + items = get_completion_items("active_person.", 1, len("active_person."), namespace) + matching = [i for i in items if i["name"] == "primary_name"] + self.assertEqual(matching, [{"name": "primary_name", "complete": "primary_name"}]) + + +class TestRobustness(_MockSaBase): + def test_empty_source_does_not_raise(self): + # Completing on an empty buffer legitimately lists every builtin + # in scope; the point of this test is only that it doesn't raise. + names = self._complete("", {}) + self.assertIn("print", names) + + def test_incomplete_code_does_not_raise(self): + # Mid-typing code is often syntactically invalid; must not crash. + names = self._complete("for person in people(", {}) + self.assertIsInstance(names, list) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampyScript/tests/test_completion_popup.py b/GrampyScript/tests/test_completion_popup.py new file mode 100644 index 000000000..bcb788daa --- /dev/null +++ b/GrampyScript/tests/test_completion_popup.py @@ -0,0 +1,201 @@ +""" +Tests for completion_popup.py — the Tab-triggered completion popover. + +Needs a real (possibly virtual, e.g. Xvfb) display since it builds real +Gtk widgets (Gtk.Popover, Gtk.ListBox) and asks for their allocation. +""" + +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import gi + +gi.require_version("Gtk", "3.0") +gi.require_version("Gdk", "3.0") +from gi.repository import Gdk, Gtk + +from completion_popup import CompletionController + + +class _FakeEvent: + def __init__(self, keyval): + self.keyval = keyval + + +def _make_controller(text, cursor_offset=None, namespace=None): + textview = Gtk.TextView() + buffer = textview.get_buffer() + buffer.set_text(text) + if cursor_offset is None: + cursor_offset = len(text) + buffer.place_cursor(buffer.get_iter_at_offset(cursor_offset)) + + # A real (offscreen) top-level window so widgets can be allocated -- + # Gtk.Popover needs a realized relative_to widget to compute a + # position against. + window = Gtk.Window() + window.add(textview) + window.set_default_size(400, 300) + window.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() + + controller = CompletionController(textview, get_namespace=lambda: namespace or {}) + return controller, buffer, window + + +class TestTriggerAndClose(unittest.TestCase): + def test_trigger_opens_for_completable_context(self): + controller, buffer, window = _make_controller("active_person.") + opened = controller.trigger() + self.assertTrue(opened) + self.assertTrue(controller.is_open()) + names = [item["name"] for item in controller.items] + self.assertIn("primary_name", names) + window.destroy() + + def test_trigger_does_not_open_after_whitespace(self): + controller, buffer, window = _make_controller("x = 1 ") + opened = controller.trigger() + self.assertFalse(opened) + self.assertFalse(controller.is_open()) + window.destroy() + + def test_trigger_does_not_open_on_empty_buffer(self): + controller, buffer, window = _make_controller("") + opened = controller.trigger() + self.assertFalse(opened) + window.destroy() + + def test_close_resets_state(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + controller.close() + self.assertFalse(controller.is_open()) + self.assertEqual(controller.items, []) + self.assertEqual(controller.selected_index, 0) + window.destroy() + + +class TestAccept(unittest.TestCase): + def test_accept_inserts_missing_suffix_only(self): + controller, buffer, window = _make_controller("active_person.primary_") + controller.trigger() + # first match should be primary_name (only dynamic key matching) + names = [item["name"] for item in controller.items] + self.assertEqual(names, ["primary_name"]) + controller.accept() + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "active_person.primary_name") + self.assertFalse(controller.is_open()) + window.destroy() + + def test_accept_with_no_items_just_closes(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + controller.items = [] + controller.accept() + self.assertFalse(controller.is_open()) + window.destroy() + + +class TestNavigation(unittest.TestCase): + def test_move_selection_clamped(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + n = len(controller.items) + self.assertGreater(n, 1) + controller.move_selection(-1) + self.assertEqual(controller.selected_index, 0) + controller.move_selection(10**6) + self.assertEqual(controller.selected_index, n - 1) + controller.move_selection(-(10**6)) + self.assertEqual(controller.selected_index, 0) + window.destroy() + + +class TestOnKeyPress(unittest.TestCase): + def test_tab_opens_then_accepts(self): + controller, buffer, window = _make_controller("active_person.primary_") + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) + self.assertTrue(consumed) + self.assertTrue(controller.is_open()) + + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) + self.assertTrue(consumed) + self.assertFalse(controller.is_open()) + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "active_person.primary_name") + window.destroy() + + def test_tab_falls_through_when_nothing_completable(self): + controller, buffer, window = _make_controller("x = 1 ") + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) + self.assertFalse(consumed) # caller should fall back to inserting spaces + window.destroy() + + def test_arrow_keys_only_consumed_while_open(self): + controller, buffer, window = _make_controller("active_person.") + self.assertFalse(controller.on_key_press(_FakeEvent(Gdk.KEY_Down))) + controller.trigger() + self.assertTrue(controller.on_key_press(_FakeEvent(Gdk.KEY_Down))) + self.assertEqual(controller.selected_index, 1) + window.destroy() + + def test_escape_closes_and_is_consumed(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Escape)) + self.assertTrue(consumed) + self.assertFalse(controller.is_open()) + window.destroy() + + def test_left_right_close_but_are_not_consumed(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Left)) + self.assertFalse(consumed) # cursor movement must still happen + self.assertFalse(controller.is_open()) + window.destroy() + + def test_return_accepts_only_while_open(self): + controller, buffer, window = _make_controller("active_person.primary_") + # popover not open: Return must not be swallowed (newline/apply-script bindings) + self.assertFalse(controller.on_key_press(_FakeEvent(Gdk.KEY_Return))) + controller.trigger() + self.assertTrue(controller.on_key_press(_FakeEvent(Gdk.KEY_Return))) + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "active_person.primary_name") + window.destroy() + + +class TestLiveRefresh(unittest.TestCase): + def test_refresh_narrows_as_more_is_typed(self): + controller, buffer, window = _make_controller("active_person.") + controller.trigger() + self.assertGreater(len(controller.items), 1) + + it = buffer.get_iter_at_mark(buffer.get_insert()) + buffer.insert(it, "primary_") + controller.on_buffer_changed() + names = [item["name"] for item in controller.items] + self.assertEqual(names, ["primary_name"]) + window.destroy() + + def test_refresh_closes_when_context_no_longer_completable(self): + controller, buffer, window = _make_controller("active_person.primary_") + controller.trigger() + self.assertTrue(controller.is_open()) + + it = buffer.get_iter_at_mark(buffer.get_insert()) + buffer.insert(it, " ") + controller.on_buffer_changed() + self.assertFalse(controller.is_open()) + window.destroy() + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampyScript/tests/test_namespace_builder.py b/GrampyScript/tests/test_namespace_builder.py new file mode 100644 index 000000000..5efc008e3 --- /dev/null +++ b/GrampyScript/tests/test_namespace_builder.py @@ -0,0 +1,46 @@ +""" +Tests for namespace_builder.py — the runtime completion namespace. +""" + +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from gramps.gen.lib import Date + +from namespace_builder import build_namespace + + +class TestBuildNamespace(unittest.TestCase): + def test_without_database(self): + namespace = build_namespace() + self.assertIn("today", namespace) + self.assertIn("counter", namespace) + self.assertNotIn("database", namespace) + + def test_today_is_a_real_date(self): + namespace = build_namespace() + self.assertIsInstance(namespace["today"], Date) + + def test_counter_returns_defaultdict(self): + namespace = build_namespace() + counter = namespace["counter"]() + self.assertEqual(counter["anything"], 0) + + def test_database_included_when_given(self): + sentinel = object() + namespace = build_namespace(sentinel) + self.assertIs(namespace["database"], sentinel) + + def test_active_names_are_not_included(self): + # active_person etc. are handled as static stub annotations + # (stub_generator.ACTIVE_VARIABLES), not live namespace objects. + namespace = build_namespace() + self.assertNotIn("active_person", namespace) + self.assertNotIn("active_family", namespace) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampyScript/tests/test_stub_generator.py b/GrampyScript/tests/test_stub_generator.py new file mode 100644 index 000000000..3467b6215 --- /dev/null +++ b/GrampyScript/tests/test_stub_generator.py @@ -0,0 +1,165 @@ +""" +Tests for stub_generator.py — deriving jedi completion stubs from Gramps' +own get_schema(). +""" + +import ast +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from stub_generator import ACTIVE_VARIABLES, GENERATOR_ROW_TYPES, build_registry, render_stub_source +from completion import get_completions + + +class TestBuildRegistry(unittest.TestCase): + def setUp(self): + self.registry = build_registry() + + def test_discovers_root_types(self): + for name in ["Person", "Family", "Event", "Place", "Source", "Citation", "Note", "Media"]: + self.assertIn(name, self.registry) + + def test_discovers_nested_types(self): + # Reached only by walking into Person's primary_name / Name's date. + for name in ["Name", "Surname", "Date"]: + self.assertIn(name, self.registry) + + def test_sanitizes_titles_with_spaces(self): + # Raw schema titles are "Event reference", "Child Reference", etc. + self.assertIn("EventReference", self.registry) + self.assertNotIn("Event reference", self.registry) + + def test_person_has_raw_schema_fields(self): + fields = self.registry["Person"] + self.assertEqual(fields["gramps_id"], "str") + self.assertEqual(fields["primary_name"], "Name") + + def test_nested_list_field_is_typed(self): + fields = self.registry["Person"] + self.assertEqual(fields["address_list"], 'list["Address"]') + + def test_computed_properties_layered_on_every_type(self): + # DataDict2's @property names apply to every wrapped record, not + # just Person, since it is the same class for every nested value. + for name in ["Person", "Family", "Name"]: + self.assertEqual(self.registry[name]["father"], "Person") + + def test_computed_property_overrides_raw_field(self): + # `gender` is both a raw int field and a DataDict2 @property; + # the property wins at real attribute-lookup time. + self.assertEqual(self.registry["Person"]["gender"], "str") + + def test_class_key_excluded(self): + self.assertNotIn("_class", self.registry["Person"]) + + +class TestRenderStubSource(unittest.TestCase): + def test_output_is_valid_python(self): + source = render_stub_source(build_registry()) + ast.parse(source) # raises SyntaxError on failure + + def test_generator_functions_present(self): + source = render_stub_source(build_registry()) + for func_name, row_type in GENERATOR_ROW_TYPES.items(): + self.assertIn("def %s() -> Iterator[%s]: ..." % (func_name, row_type), source) + + def test_empty_registry_still_valid(self): + source = render_stub_source({}, generator_row_types={}, table_functions={}) + ast.parse(source) + + def test_table_functions_present(self): + source = render_stub_source(build_registry()) + self.assertIn("def selected(table_name: str) -> Iterator[Union[", source) + self.assertIn("def filtered(table_name: str) -> Iterator[Union[", source) + self.assertIn( + 'def custom_filter(name: str, namespace: str = "Person") -> Iterator[Union[', + source, + ) + + def test_table_function_union_covers_every_row_type(self): + source = render_stub_source(build_registry()) + line = next(l for l in source.splitlines() if l.startswith("def selected")) + for row_type in GENERATOR_ROW_TYPES.values(): + self.assertIn(row_type, line) + + def test_no_table_functions_when_omitted(self): + source = render_stub_source(build_registry(), table_functions={}) + self.assertNotIn("def selected", source) + + def test_active_variables_present(self): + source = render_stub_source(build_registry()) + for var_name, row_type in ACTIVE_VARIABLES.items(): + self.assertIn("%s: %s" % (var_name, row_type), source) + + def test_no_active_variables_when_omitted(self): + source = render_stub_source(build_registry(), active_variables={}) + self.assertNotIn("active_person:", source) + + +class TestActiveVariableCompletion(unittest.TestCase): + """ + active_person/active_family/etc. are declared as bare static + annotations (no value) rather than bound to a live template + DataDict2 instance. A live template would need jedi to actually call + DataDict2's computed @property methods (father, birth, ...) to see + what they return -- real SimpleAccess execution that, for a blank + template with nothing to find, only yields empty completions anyway. + """ + + def _complete(self, source): + lines = source.splitlines() + return get_completions(source, len(lines), len(lines[-1]), {}) + + def test_completes_active_person_directly(self): + names = self._complete("active_person.") + self.assertIn("primary_name", names) + self.assertIn("gramps_id", names) + + def test_completes_through_computed_property_chain(self): + # father is a DataDict2 @property, not a raw schema field -- + # this only works because it's typed in the stub, not executed. + names = self._complete("active_person.father.primary_name.first_") + self.assertEqual(names, ["first_name"]) + + def test_distinguishes_active_family_from_active_person(self): + names = self._complete("active_family.") + self.assertIn("father_handle", names) + self.assertNotIn("primary_name", names) + + +class TestTableFunctionCompletion(unittest.TestCase): + """ + selected()/filtered()/custom_filter() pick their row type from a + runtime string argument, which jedi cannot discriminate via + typing.overload + Literal (verified empirically -- it merges every + overload regardless of the literal passed). These are typed as + returning the union of every row type instead, so completion still + offers real fields rather than nothing. + """ + + def _complete(self, source): + lines = source.splitlines() + return get_completions(source, len(lines), len(lines[-1]), {}) + + def test_selected_offers_real_fields(self): + names = self._complete('for person in selected("Person"):\n person.') + self.assertIn("primary_name", names) + + def test_filtered_offers_real_fields(self): + names = self._complete('for fam in filtered("Family"):\n fam.') + self.assertIn("father_handle", names) + + def test_custom_filter_offers_real_fields_with_default_namespace(self): + names = self._complete('for person in custom_filter("example filter"):\n person.') + self.assertIn("primary_name", names) + + def test_custom_filter_offers_real_fields_with_explicit_namespace(self): + names = self._complete('for fam in custom_filter("f", "Family"):\n fam.') + self.assertIn("father_handle", names) + + +if __name__ == "__main__": + unittest.main() From f7e5b92656a266c61487c9b97d99dc5fe07f2e9d Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 15:20:27 -0700 Subject: [PATCH 009/156] Fix status label forcing the GrampyScript gramplet wider than its container statusmsg's text sometimes embeds the current file's full path (e.g. "Loaded '/home/.../scripts/some_script.gram.py'"), and an unbounded Gtk.Label requests enough natural width to fit that whole string, which was pushing the gramplet wider than its panel and forcing horizontal scrolling. Capping it with set_ellipsize()/ set_max_width_chars() bounds the natural width regardless of message content. Also reverts the wrap-mode/scroll-policy change from the previous commit, which guessed the code editor's TextView was the cause; it wasn't, and auto-wrapping code isn't desirable anyway since it breaks visual alignment of indentation. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 377fb959d..f85fff7c7 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -369,9 +369,7 @@ def build_gui(self): self.editor = Gtk.ScrolledWindow() self.editor.set_shadow_type(Gtk.ShadowType.IN) - self.editor.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.editor_textview = Gtk.TextView() - self.editor_textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) self.editor.add(self.editor_textview) font_desc = self.editor_textview.get_pango_context().get_font_description() font_desc.set_family( @@ -474,6 +472,15 @@ def build_gui(self): self.statusmsg = Gtk.Label(_("Ready...")) self.statusmsg.set_xalign(0) # 0.0 for left, 0.5 for center, 1.0 for right + # Some status messages embed the full path of the current file + # (e.g. "Loaded '/home/.../scripts/some_script.gram.py'"), which + # would otherwise force the whole gramplet wider than its + # container. Ellipsize and cap the natural width so it truncates + # instead -- max_width_chars is what actually bounds the natural + # size request; ellipsize alone only takes effect once allocated + # space is already smaller than that. + self.statusmsg.set_ellipsize(Pango.EllipsizeMode.MIDDLE) + self.statusmsg.set_max_width_chars(40) self.statusmsg.get_style_context().add_class('bordered-label') #add a css class self.statusmsg.get_style_context().add_provider( provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION From 6fef53862190dfe657be9b9dd0da972cb46eea21 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 15:39:24 -0700 Subject: [PATCH 010/156] Add a Help item to the GrampyScript Script menu Opens the addon's wiki help page (Addon:GrampyScript), reusing the same help_url the gramplet already exposes via self.gui. --- GrampyScript/GrampyScript.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index f85fff7c7..fed6a1e4f 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -45,6 +45,7 @@ from gramps.gui.utils import match_primary_mask from gramps.gen.config import config as configman from gramps.gui.dialog import OkDialog, ErrorDialog, SaveDialog +from gramps.gui.display import display_help, display_url from gramps.gui.editors import ( EditCitation, EditEvent, @@ -336,15 +337,19 @@ def build_gui(self): openitem = Gtk.MenuItem(label=_("Open...")) save_item = Gtk.MenuItem(label=_("Save")) save_as_item = Gtk.MenuItem(label=_("Save as...")) + help_item = Gtk.MenuItem(label=_("Help")) filemenu.append(newitem) filemenu.append(openitem) filemenu.append(save_item) filemenu.append(save_as_item) + filemenu.append(Gtk.SeparatorMenuItem()) + filemenu.append(help_item) menubar.append(fileitem) newitem.connect("activate", self.new_script) openitem.connect("activate", self.open_script) save_as_item.connect("activate", self.save_as_script) save_item.connect("activate", self.save_script) + help_item.connect("activate", self.show_help) datamenu = Gtk.Menu() dataitem = Gtk.MenuItem(label=_("Data")) @@ -607,6 +612,13 @@ def save_as_script(self, widget): choose_file_dialog.destroy() + def show_help(self, widget): + help_url = self.gui.help_url + if help_url and help_url.startswith(("http://", "https://")): + display_url(help_url) + else: + display_help(help_url) + def save_csv(self, widget): if self.liststore is None: self.statusmsg.set_text("No data to save") From c24b6fc64011d9d6c3247b45a2304b3f2ba8533f Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 16:11:56 -0700 Subject: [PATCH 011/156] Make example script descriptions self-hosted and generated Each scripts/*.gram.py file now carries its own description as a module docstring; script_descriptions.py is fully regenerated from those docstrings (plus each file's title comment) via update_script_descriptions.py, instead of hand-maintaining translated text disconnected from the examples it describes. Also fix the editor's syntax highlighter, which had no notion of string literals and was bolding keywords found inside quoted strings (most visibly inside the new docstrings). Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 42 ++-- GrampyScript/script_descriptions.py | 92 +++++---- GrampyScript/scripts/01_list_people.gram.py | 4 + .../scripts/02_filter_by_surname.gram.py | 4 + .../scripts/03_family_overview.gram.py | 4 + .../scripts/04_gender_pie_chart.gram.py | 4 + GrampyScript/scripts/05_age_histogram.gram.py | 5 + .../06_mark_unsourced_people_private.gram.py | 5 + .../scripts/07_csv_ready_report.gram.py | 5 + .../scripts/08_active_person_summary.gram.py | 4 + .../scripts/09_selected_people_report.gram.py | 4 + .../10_find_missing_birth_dates.gram.py | 4 + .../scripts/11_import_example.gram.py | 5 + .../scripts/12_custom_filter_example.gram.py | 6 + .../13_delete_unused_repositories.gram.py | 6 + .../tests/test_script_descriptions.py | 21 ++ GrampyScript/update_script_descriptions.py | 184 ++++++++---------- 17 files changed, 243 insertions(+), 156 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index fed6a1e4f..ece0f7035 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -411,6 +411,7 @@ def build_gui(self): self.comment_tag = self.ebuf.create_tag( "comment", foreground="gray", style=Pango.Style.ITALIC ) + self.string_tag = self.ebuf.create_tag("string", foreground="brown") self.ebuf.connect("changed", self.on_buffer_changed) self.completion = CompletionController( self.editor_textview, get_namespace=lambda: build_namespace(self.dbstate.db) @@ -699,37 +700,56 @@ def highlight_syntax(self): text = self.ebuf.get_text(start_iter, end_iter, True) + def inside_span(match, spans): + start_offset = match.start() + end_offset = match.end() + for span_start, span_end in spans: + if start_offset >= span_start and end_offset <= span_end: + return True + return False + + # Strings are found first so that keywords/comments inside them (eg. + # "with" in a docstring, or a "#" in a quoted string) aren't + # mistaken for code. + string_pattern = ( + r'"""[\s\S]*?"""' + r"|'''[\s\S]*?'''" + r'|"(?:[^"\\\n]|\\.)*"' + r"|'(?:[^'\\\n]|\\.)*'" + ) + string_matches = [] + for match in re.finditer(string_pattern, text): + start = self.ebuf.get_iter_at_offset(match.start()) + end = self.ebuf.get_iter_at_offset(match.end()) + self.ebuf.apply_tag(self.string_tag, start, end) + string_matches.append((match.start(), match.end())) + comment_matches = [] for match in re.finditer(r"#.*", text): + if inside_span(match, string_matches): + continue start = self.ebuf.get_iter_at_offset(match.start()) end = self.ebuf.get_iter_at_offset(match.end()) self.ebuf.apply_tag(self.comment_tag, start, end) comment_matches.append((match.start(), match.end())) - def inside_comment(match): - start_offset = match.start() - end_offset = match.end() - # Check if the keyword overlaps with a comment - for comment_start, comment_end in comment_matches: - if start_offset >= comment_start and end_offset <= comment_end: - return True - return False + skip_spans = string_matches + comment_matches for keyword in self.keywords: for match in re.finditer(r"\b" + keyword + r"\b", text): - if not inside_comment(match): + if not inside_span(match, skip_spans): start = self.ebuf.get_iter_at_offset(match.start()) end = self.ebuf.get_iter_at_offset(match.end()) self.ebuf.apply_tag(self.keyword_tag, start, end) for constant in self.constants: for match in re.finditer(r"\b" + constant + r"\b", text): - if not inside_comment(match): + if not inside_span(match, skip_spans): start = self.ebuf.get_iter_at_offset(match.start()) end = self.ebuf.get_iter_at_offset(match.end()) self.ebuf.apply_tag(self.constant_tag, start, end) for function in self.functions: for match in re.finditer(r"\b" + function + r"\b", text): - if not inside_comment(match): + if not inside_span(match, skip_spans): start = self.ebuf.get_iter_at_offset(match.start()) end = self.ebuf.get_iter_at_offset(match.end()) self.ebuf.apply_tag(self.function_tag, start, end) diff --git a/GrampyScript/script_descriptions.py b/GrampyScript/script_descriptions.py index 9c43e50f3..1e35346d3 100644 --- a/GrampyScript/script_descriptions.py +++ b/GrampyScript/script_descriptions.py @@ -19,9 +19,20 @@ """ Translatable titles and descriptions for the bundled example scripts in -scripts/. Kept out of the .gram.py files themselves so the examples stay -free of gettext markup while still being picked up by the addon's normal -xgettext-based translation pipeline (see ../make.py). +scripts/. This file is generated -- do not hand-edit the SCRIPT_DESCRIPTIONS +dict below. Each entry's title comes from the corresponding script's leading +'# Title' comment, and its description comes from that script's module +docstring; both are kept in the .gram.py files themselves so the source of +truth lives next to the code it documents. + +Wrapping these plain-text strings in _() here (rather than in the .gram.py +files) is what makes them picked up by the addon's normal xgettext-based +translation pipeline (see ../make.py) while keeping the example scripts +themselves free of gettext markup. + +To pick up a new script, or a script's changed title/docstring, run: + + python3 update_script_descriptions.py """ from gramps.gen.const import GRAMPS_LOCALE as glocale @@ -32,41 +43,39 @@ "01_list_people.gram.py": ( _("List All People"), _( - "Iterate over every person in the database and show their " - "Gramps ID, given name, surname, and gender in the results " - "table." + "Iterate over every person in the database and show their Gramps " + "ID, given name, surname, and gender in the results table." ), ), "02_filter_by_surname.gram.py": ( _("Filter By Surname"), _( - "List only the people whose surname matches a given value — " - "a starting point for narrowing any report down by a " - "condition." + "List only the people whose surname matches a given value — a " + "starting point for narrowing any report down by a condition." ), ), "03_family_overview.gram.py": ( _("Family Overview"), _( - "List every family together with the father, the mother, and " - "how many children they have — a quick way to spot families " - "that look incomplete." + "List every family together with the father, the mother, and how " + "many children they have — a quick way to spot families that look " + "incomplete." ), ), "04_gender_pie_chart.gram.py": ( _("Gender Breakdown (Pie Chart)"), _( - "Count how many people are male, female, or of unknown " - "gender, then draw a pie chart of the totals. Check the " - "Chart tab after running." + "Count how many people are male, female, or of unknown gender, " + "then draw a pie chart of the totals. Check the Chart tab after " + "running." ), ), "05_age_histogram.gram.py": ( _("Age At Death Histogram"), _( "For everyone with both a birth and a death event recorded, " - "compute their age in whole years and draw a histogram of " - "the distribution. Check the Chart tab after running." + "compute their age in whole years and draw a histogram of the " + "distribution. Check the Chart tab after running." ), ), "06_mark_unsourced_people_private.gram.py": ( @@ -81,63 +90,60 @@ "07_csv_ready_report.gram.py": ( _("CSV-Ready People Report"), _( - "Build a simple tabular report — ID, name, gender, birth " - "year — for every person. Once it runs, use Data > Save as " - "CSV or Copy to clipboard to export the Table tab's " - "contents." + "Build a simple tabular report — ID, name, gender, birth year — " + "for every person. Once it runs, use Data > Save as CSV or Copy to " + "clipboard to export the Table tab's contents." ), ), "08_active_person_summary.gram.py": ( _("Active Person Summary"), _( - "Show a compact family summary for the currently active " - "person: their record, parents, spouse, and children." + "Show a compact family summary for the currently active person: " + "their record, parents, spouse, and children." ), ), "09_selected_people_report.gram.py": ( _("Report On Selected People"), _( - "List just the people currently selected (highlighted) in " - "the People view. Select some rows in the People view " - "before running this script." + "List just the people currently selected (highlighted) in the " + "People view. Select some rows in the People view before running " + "this script." ), ), "10_find_missing_birth_dates.gram.py": ( _("Find People Missing A Birth Date"), _( - "Data-quality check: list every person who has no recorded " - "birth event, so you can prioritize research on those " - "records." + "Data-quality check: list every person who has no recorded birth " + "event, so you can prioritize research on those records." ), ), "11_import_example.gram.py": ( _("Births Per Decade (Import Example)"), _( - "Counts births by decade, using a decade() function imported " - "from script_helpers.py in this same folder — a template for " - "sharing helper code between your own scripts with a plain " - "'import' statement." + "Counts births by decade, using a decade() function imported from " + "script_helpers.py in this same folder — a template for sharing " + "helper code between your own scripts with a plain 'import' " + "statement." ), ), "12_custom_filter_example.gram.py": ( _("Custom Filter Example"), _( "Runs one of your own custom filters (from the Filters " - "gramplet/editor) by name using custom_filter(). Change " - "'example filter' to the name of a filter you've already " - "created; if the name doesn't match one, a warning shows up " - "in the Output tab instead." + "gramplet/editor) by name using custom_filter(). Change 'example " + "filter' to the name of a filter you've already created; if the " + "name doesn't match one, a warning shows up in the Output tab " + "instead." ), ), "13_delete_unused_repositories.gram.py": ( _("Delete Unused Repositories (Delete Example)"), _( - "Demonstrates delete(): removes any Repository record that " - "nothing else in the tree refers to. Most trees have no " - "unused repositories, so this is unlikely to actually delete " - "anything — it's meant to show the pattern, wrapped in " - "begin_changes()/end_changes() as a single undoable " - "transaction." + "Demonstrates delete(): removes any Repository record that nothing " + "else in the tree refers to. Most trees have no unused " + "repositories, so this is unlikely to actually delete anything — " + "it's meant to show the pattern, wrapped in " + "begin_changes()/end_changes() as a single undoable transaction." ), ), } diff --git a/GrampyScript/scripts/01_list_people.gram.py b/GrampyScript/scripts/01_list_people.gram.py index 90d3fa04c..ee569eb71 100644 --- a/GrampyScript/scripts/01_list_people.gram.py +++ b/GrampyScript/scripts/01_list_people.gram.py @@ -1,4 +1,8 @@ # List All People +""" +Iterate over every person in the database and show their Gramps ID, given name, +surname, and gender in the results table. +""" for person in people(): row( diff --git a/GrampyScript/scripts/02_filter_by_surname.gram.py b/GrampyScript/scripts/02_filter_by_surname.gram.py index a1f64deee..4107b6147 100644 --- a/GrampyScript/scripts/02_filter_by_surname.gram.py +++ b/GrampyScript/scripts/02_filter_by_surname.gram.py @@ -1,4 +1,8 @@ # Filter By Surname +""" +List only the people whose surname matches a given value — a starting point for +narrowing any report down by a condition. +""" TARGET_SURNAME = "Smith" diff --git a/GrampyScript/scripts/03_family_overview.gram.py b/GrampyScript/scripts/03_family_overview.gram.py index 9574b0a97..ea8394bbb 100644 --- a/GrampyScript/scripts/03_family_overview.gram.py +++ b/GrampyScript/scripts/03_family_overview.gram.py @@ -1,4 +1,8 @@ # Family Overview +""" +List every family together with the father, the mother, and how many children +they have — a quick way to spot families that look incomplete. +""" for family in families(): row(family.gramps_id, family.father, family.mother, len(family.children)) diff --git a/GrampyScript/scripts/04_gender_pie_chart.gram.py b/GrampyScript/scripts/04_gender_pie_chart.gram.py index cf70b8008..4fe1a5120 100644 --- a/GrampyScript/scripts/04_gender_pie_chart.gram.py +++ b/GrampyScript/scripts/04_gender_pie_chart.gram.py @@ -1,4 +1,8 @@ # Gender Breakdown (Pie Chart) +""" +Count how many people are male, female, or of unknown gender, then draw a pie +chart of the totals. Check the Chart tab after running. +""" counts = counter() for person in people(): diff --git a/GrampyScript/scripts/05_age_histogram.gram.py b/GrampyScript/scripts/05_age_histogram.gram.py index 311f7b826..b959a7587 100644 --- a/GrampyScript/scripts/05_age_histogram.gram.py +++ b/GrampyScript/scripts/05_age_histogram.gram.py @@ -1,4 +1,9 @@ # Age At Death Histogram +""" +For everyone with both a birth and a death event recorded, compute their age in +whole years and draw a histogram of the distribution. Check the Chart tab after +running. +""" ages = [] for person in people(): diff --git a/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py b/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py index 65dd9855b..1783a6d21 100644 --- a/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py +++ b/GrampyScript/scripts/06_mark_unsourced_people_private.gram.py @@ -1,4 +1,9 @@ # Mark Unsourced People As Private +""" +Batch-edit example: find every person who has no citations attached and flag +them as private, wrapped in begin_changes()/end_changes() so the edits happen +inside a single, undoable transaction. +""" begin_changes("Mark unsourced people as private") diff --git a/GrampyScript/scripts/07_csv_ready_report.gram.py b/GrampyScript/scripts/07_csv_ready_report.gram.py index 2a0867b37..14b83e2f1 100644 --- a/GrampyScript/scripts/07_csv_ready_report.gram.py +++ b/GrampyScript/scripts/07_csv_ready_report.gram.py @@ -1,4 +1,9 @@ # CSV-Ready People Report +""" +Build a simple tabular report — ID, name, gender, birth year — for every +person. Once it runs, use Data > Save as CSV or Copy to clipboard to export the +Table tab's contents. +""" columns("ID", "Given Name", "Surname", "Gender", "Birth Year") diff --git a/GrampyScript/scripts/08_active_person_summary.gram.py b/GrampyScript/scripts/08_active_person_summary.gram.py index d0c2b7cb6..371315b16 100644 --- a/GrampyScript/scripts/08_active_person_summary.gram.py +++ b/GrampyScript/scripts/08_active_person_summary.gram.py @@ -1,4 +1,8 @@ # Active Person Summary +""" +Show a compact family summary for the currently active person: their record, +parents, spouse, and children. +""" person = active_person if person: diff --git a/GrampyScript/scripts/09_selected_people_report.gram.py b/GrampyScript/scripts/09_selected_people_report.gram.py index 2d6084c6d..74f840d9d 100644 --- a/GrampyScript/scripts/09_selected_people_report.gram.py +++ b/GrampyScript/scripts/09_selected_people_report.gram.py @@ -1,4 +1,8 @@ # Report On Selected People +""" +List just the people currently selected (highlighted) in the People view. +Select some rows in the People view before running this script. +""" for person in selected("Person"): row(person) diff --git a/GrampyScript/scripts/10_find_missing_birth_dates.gram.py b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py index 8e8b91dcf..eb6a21555 100644 --- a/GrampyScript/scripts/10_find_missing_birth_dates.gram.py +++ b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py @@ -1,4 +1,8 @@ # Find People Missing A Birth Date +""" +Data-quality check: list every person who has no recorded birth event, so you +can prioritize research on those records. +""" for person in people(): if not person.birth: diff --git a/GrampyScript/scripts/11_import_example.gram.py b/GrampyScript/scripts/11_import_example.gram.py index 8b0b97c8c..a01dd9089 100644 --- a/GrampyScript/scripts/11_import_example.gram.py +++ b/GrampyScript/scripts/11_import_example.gram.py @@ -1,4 +1,9 @@ # Births Per Decade (Import Example) +""" +Counts births by decade, using a decade() function imported from +script_helpers.py in this same folder — a template for sharing helper code +between your own scripts with a plain 'import' statement. +""" from script_helpers import decade diff --git a/GrampyScript/scripts/12_custom_filter_example.gram.py b/GrampyScript/scripts/12_custom_filter_example.gram.py index da212b84b..c84eeffe5 100644 --- a/GrampyScript/scripts/12_custom_filter_example.gram.py +++ b/GrampyScript/scripts/12_custom_filter_example.gram.py @@ -1,4 +1,10 @@ # Custom Filter Example +""" +Runs one of your own custom filters (from the Filters gramplet/editor) by name +using custom_filter(). Change 'example filter' to the name of a filter you've +already created; if the name doesn't match one, a warning shows up in the +Output tab instead. +""" for person in custom_filter("example filter"): row(person) diff --git a/GrampyScript/scripts/13_delete_unused_repositories.gram.py b/GrampyScript/scripts/13_delete_unused_repositories.gram.py index d9e657efc..b1f2e169e 100644 --- a/GrampyScript/scripts/13_delete_unused_repositories.gram.py +++ b/GrampyScript/scripts/13_delete_unused_repositories.gram.py @@ -1,4 +1,10 @@ # Delete Unused Repositories (Delete Example) +""" +Demonstrates delete(): removes any Repository record that nothing else in the +tree refers to. Most trees have no unused repositories, so this is unlikely to +actually delete anything — it's meant to show the pattern, wrapped in +begin_changes()/end_changes() as a single undoable transaction. +""" begin_changes("Delete unused repositories") diff --git a/GrampyScript/tests/test_script_descriptions.py b/GrampyScript/tests/test_script_descriptions.py index 77284fd20..a0df5b6b2 100644 --- a/GrampyScript/tests/test_script_descriptions.py +++ b/GrampyScript/tests/test_script_descriptions.py @@ -14,6 +14,12 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from script_descriptions import SCRIPT_DESCRIPTIONS +from update_script_descriptions import ( + DESCRIPTIONS_PATH, + _load_header, + build_source, + collect_entries, +) SCRIPTS_DIR = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts" @@ -44,6 +50,21 @@ def test_entries_have_title_and_description(self): self.assertTrue(description.strip(), "%s has an empty description" % name) +class TestScriptDescriptionsIsGenerated(unittest.TestCase): + def test_regenerating_produces_no_changes(self): + entries, errors = collect_entries() + self.assertEqual(errors, []) + header = _load_header(DESCRIPTIONS_PATH) + regenerated = build_source(entries, header) + on_disk = open(DESCRIPTIONS_PATH, encoding="utf-8").read() + self.assertEqual( + regenerated, + on_disk, + "script_descriptions.py is out of sync with scripts/*.gram.py -- " + "run `python3 update_script_descriptions.py` to regenerate it.", + ) + + class TestScriptsAreValidPython(unittest.TestCase): def test_all_scripts_parse(self): for path in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")): diff --git a/GrampyScript/update_script_descriptions.py b/GrampyScript/update_script_descriptions.py index 4652e7eec..d8f508362 100644 --- a/GrampyScript/update_script_descriptions.py +++ b/GrampyScript/update_script_descriptions.py @@ -18,35 +18,29 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ -Dev tool: keep script_descriptions.py's SCRIPT_DESCRIPTIONS dict in sync -with the files actually present in scripts/. +Dev tool: rebuild script_descriptions.py's SCRIPT_DESCRIPTIONS dict from the +files in scripts/. Each scripts/*.gram.py file is the source of truth for +its own entry: the title comes from the file's leading '# Title' comment, +and the description comes from its module docstring (the triple-quoted +string right below that comment). -Run it after adding a new example script, deleting one, or renaming one: +Run it any time you add a new example script, edit an existing script's +title or docstring, or delete a script: python3 update_script_descriptions.py -What it does automatically (safe, structural, nothing to lose): - - Adds a stub entry -- title taken from the new file's leading '#' - comment, description a "TODO" placeholder -- for any scripts/*.gram.py - file with no entry yet. - - Drops entries for files that no longer exist in scripts/. - -What it only *warns* about (needs a human judgment call): - - A file whose leading comment title no longer matches the title - already catalogued in SCRIPT_DESCRIPTIONS. Titles are not - auto-overwritten, since the catalogued one may have been deliberately - written differently (and richer) than the terse in-file comment. - -Existing entries' source text (title + description, translator comments, -line wrapping, quoting) is preserved byte-for-byte by slicing it straight -out of the current file with ast -- this script never touches wording it -didn't generate itself. +The whole SCRIPT_DESCRIPTIONS body is always fully rebuilt from scripts/ -- +there is no hand-maintained text left to preserve. A script missing a +title comment or a docstring is an error, since both are now required. +The static header (license block, module docstring, imports) is kept as +whatever's already at the top of script_descriptions.py. """ import ast import glob import os import sys +import textwrap from script_utils import SCRIPTS_DIR, extract_header_comment @@ -54,113 +48,99 @@ os.path.dirname(os.path.abspath(__file__)), "script_descriptions.py" ) -STUB_DESCRIPTION = "TODO: describe what this script does." +ENTRY_INDENT = " " * 8 +TEXT_INDENT = " " * 12 +DESCRIPTION_WIDTH = 65 -def _slice_source(lines, node): - start_line, start_col = node.lineno - 1, node.col_offset - end_line, end_col = node.end_lineno - 1, node.end_col_offset - if start_line == end_line: - return lines[start_line][start_col:end_col] - parts = [lines[start_line][start_col:]] - parts.extend(lines[start_line + 1 : end_line]) - parts.append(lines[end_line][:end_col]) - return "".join(parts) - - -def _load_existing(path): - """ - Returns (header, entries) where header is the file text up through - "SCRIPT_DESCRIPTIONS = {" and entries maps filename -> (title, - raw_value_source) using the tuple's exact original source text. - """ +def _load_header(path): + """Return the file text up through the "SCRIPT_DESCRIPTIONS = {" line.""" source = open(path, encoding="utf-8").read() tree = ast.parse(source) lines = source.splitlines(keepends=True) - dict_node = None for node in ast.walk(tree): if isinstance(node, ast.Assign) and any( isinstance(t, ast.Name) and t.id == "SCRIPT_DESCRIPTIONS" for t in node.targets ): - dict_node = node.value - break - if dict_node is None: - raise RuntimeError("SCRIPT_DESCRIPTIONS not found in %s" % path) + return "".join(lines[: node.lineno - 1]) + "SCRIPT_DESCRIPTIONS = {\n" + raise RuntimeError("SCRIPT_DESCRIPTIONS not found in %s" % path) - header = "".join(lines[: dict_node.lineno - 1]) + "SCRIPT_DESCRIPTIONS = {\n" - entries = {} - for key_node, value_node in zip(dict_node.keys, dict_node.values): - filename = ast.literal_eval(key_node) - title = value_node.elts[0].args[0].value - raw_value = _slice_source(lines, value_node) - entries[filename] = (title, raw_value) - return header, entries +def _dquote(text): + return '"' + text.replace("\\", "\\\\").replace('"', '\\"') + '"' -def _stub_entry(title): - return '(\n _(%r),\n _(%r),\n )' % (title, STUB_DESCRIPTION) +def _render_call(text, width=None): + lines = textwrap.wrap(text, width=width) if width else [text] + if len(lines) <= 1: + return "_(%s)" % _dquote(text) + body = "".join( + "%s%s\n" % (TEXT_INDENT, _dquote(line + (" " if i < len(lines) - 1 else ""))) + for i, line in enumerate(lines) + ) + return "_(\n%s%s)" % (body, ENTRY_INDENT) -def main(): - header, existing = _load_existing(DESCRIPTIONS_PATH) +def render_entry(filename, title, description): + return " %s: (\n%s%s,\n%s%s,\n ),\n" % ( + _dquote(filename), + ENTRY_INDENT, + _render_call(title), + ENTRY_INDENT, + _render_call(description, width=DESCRIPTION_WIDTH), + ) - current_files = sorted( - os.path.basename(p) - for p in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")) + +def collect_entries(): + """ + Returns (entries, errors), where entries maps filename -> (title, + description) read from scripts/*.gram.py, and errors lists filenames + missing a title comment or a docstring. + """ + entries = {} + errors = [] + for path in sorted(glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py"))): + filename = os.path.basename(path) + source = open(path, encoding="utf-8").read() + title = extract_header_comment(source) + description = ast.get_docstring(ast.parse(source), clean=True) + if description: + description = " ".join(description.split()) + if not title: + errors.append("%s: missing a leading '# Title' comment" % filename) + if not description: + errors.append("%s: missing a description docstring" % filename) + if title and description: + entries[filename] = (title, description) + return entries, errors + + +def build_source(entries, header): + body = "".join( + render_entry(filename, title, description) + for filename, (title, description) in entries.items() ) + return header + body + "}\n" + + +def main(): + entries, errors = collect_entries() + if errors: + print("Cannot regenerate script_descriptions.py:") + for error in errors: + print(" ! %s" % error) + return 1 - added, removed, retitled_warnings = [], [], [] - - body_lines = [] - for filename in current_files: - file_title = extract_header_comment( - open(os.path.join(SCRIPTS_DIR, filename)).read() - ) - if filename in existing: - title, raw_value = existing[filename] - if file_title and file_title != title: - retitled_warnings.append((filename, title, file_title)) - else: - title, raw_value = file_title or filename, _stub_entry( - file_title or filename - ) - added.append(filename) - body_lines.append(' "%s": %s,\n' % (filename, raw_value)) - - for filename in existing: - if filename not in current_files: - removed.append(filename) - - new_source = header + "".join(body_lines) + "}\n" + header = _load_header(DESCRIPTIONS_PATH) + new_source = build_source(entries, header) with open(DESCRIPTIONS_PATH, "w", encoding="utf-8") as fp: fp.write(new_source) - if added: - print("Added stub entries (fill in real descriptions):") - for filename in added: - print(" + %s" % filename) - if removed: - print("Removed stale entries (file no longer in scripts/):") - for filename in removed: - print(" - %s" % filename) - if retitled_warnings: - print("Title mismatches (file comment changed, catalogued title did not):") - for filename, old_title, new_title in retitled_warnings: - print(" ! %s" % filename) - print(" catalogued: %r" % old_title) - print(" in file: %r" % new_title) - print( - " -> update the title in script_descriptions.py by hand if the " - "file's title is now the correct one." - ) - if not (added or removed or retitled_warnings): - print("script_descriptions.py is already in sync with scripts/.") - - return 1 if retitled_warnings else 0 + print("Regenerated script_descriptions.py from %d scripts." % len(entries)) + return 0 if __name__ == "__main__": From e2640e0a3d7b1b86627b0bcc64a9014f9b480e53 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 19:40:09 -0700 Subject: [PATCH 012/156] Append () to function completions in GrampyScript editor Completions for callables (people(), families(), custom_filter(), dict methods, etc.) now insert with parens, landing the cursor between them when the function takes arguments. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/completion.py | 45 +++++++++++++++++---- GrampyScript/completion_popup.py | 5 +++ GrampyScript/tests/test_completion.py | 31 +++++++++++++- GrampyScript/tests/test_completion_popup.py | 26 ++++++++++++ 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/GrampyScript/completion.py b/GrampyScript/completion.py index 76950d945..54439d946 100644 --- a/GrampyScript/completion.py +++ b/GrampyScript/completion.py @@ -77,16 +77,45 @@ def get_completions(source, line, column, namespace): return [completion.name for completion in _complete(source, line, column, namespace)] +def _takes_arguments(completion): + """True if a function/method completion has at least one parameter + to fill in (jedi's Signature.params already excludes a bound + method's `self`), used to decide whether the inserted "()" should + land the cursor between the parens instead of after them.""" + try: + signatures = completion.get_signatures() + except Exception: + return False + return any(signature.params for signature in signatures) + + def get_completion_items(source, line, column, namespace): """ Same as get_completions(), but for UI use: returns a list of {"name": full completion name, "complete": text to insert at the - cursor} dicts. `name` is for display; `complete` is only the - remaining characters jedi says are missing (e.g. typing "impo" and - accepting "import" gives complete == "rt"), so callers can insert it - directly without recomputing/re-typing the already-typed prefix. + cursor, "cursor_offset": how many characters back from the end of + the inserted text the cursor should land} dicts. `name` is for + display; `complete` is only the remaining characters jedi says are + missing (e.g. typing "impo" and accepting "import" gives complete == + "rt"), so callers can insert it directly without + recomputing/re-typing the already-typed prefix. + + Function/method completions (jedi type "function", e.g. `people`, + `families`) get "()" appended to both `name` (so the popup reads + "people()") and `complete`; `cursor_offset` is then 1 for functions + that take arguments, landing the cursor between the parens ready to + type them, or 0 for no-argument functions, landing it after the + closing paren. """ - return [ - {"name": completion.name, "complete": completion.complete} - for completion in _complete(source, line, column, namespace) - ] + items = [] + for completion in _complete(source, line, column, namespace): + name = completion.name + complete = completion.complete + cursor_offset = 0 + if completion.type == "function": + name += "()" + complete += "()" + if _takes_arguments(completion): + cursor_offset = 1 + items.append({"name": name, "complete": complete, "cursor_offset": cursor_offset}) + return items diff --git a/GrampyScript/completion_popup.py b/GrampyScript/completion_popup.py index 21a7e4546..9646da588 100644 --- a/GrampyScript/completion_popup.py +++ b/GrampyScript/completion_popup.py @@ -217,6 +217,11 @@ def accept(self): if self.items: item = self.items[self.selected_index] self.buffer.insert(self._cursor_iter(), item["complete"]) + offset = item.get("cursor_offset", 0) + if offset: + it = self._cursor_iter() + it.backward_chars(offset) + self.buffer.place_cursor(it) self.close() def close(self): diff --git a/GrampyScript/tests/test_completion.py b/GrampyScript/tests/test_completion.py index 70c43a5fb..12ce86ddc 100644 --- a/GrampyScript/tests/test_completion.py +++ b/GrampyScript/tests/test_completion.py @@ -119,13 +119,40 @@ class TestCompletionItems(_MockSaBase): def test_complete_is_only_the_missing_suffix(self): namespace = {"active_person": DataDict2(_make_person())} items = get_completion_items("active_person.primary_", 1, len("active_person.primary_"), namespace) - self.assertEqual(items, [{"name": "primary_name", "complete": "name"}]) + self.assertEqual( + items, [{"name": "primary_name", "complete": "name", "cursor_offset": 0}] + ) def test_complete_is_full_name_when_nothing_typed_yet(self): namespace = {"active_person": DataDict2(_make_person())} items = get_completion_items("active_person.", 1, len("active_person."), namespace) matching = [i for i in items if i["name"] == "primary_name"] - self.assertEqual(matching, [{"name": "primary_name", "complete": "primary_name"}]) + self.assertEqual( + matching, [{"name": "primary_name", "complete": "primary_name", "cursor_offset": 0}] + ) + + def test_no_arg_function_gets_parens_appended(self): + # people() takes no arguments -- cursor lands after "()". + items = get_completion_items("peop", 1, len("peop"), {}) + matching = [i for i in items if i["name"] == "people()"] + self.assertEqual( + matching, [{"name": "people()", "complete": "le()", "cursor_offset": 0}] + ) + + def test_function_with_args_lands_cursor_between_parens(self): + items = get_completion_items("custom_fil", 1, len("custom_fil"), {}) + matching = [i for i in items if i["name"] == "custom_filter()"] + self.assertEqual( + matching, [{"name": "custom_filter()", "complete": "ter()", "cursor_offset": 1}] + ) + + def test_non_function_completion_has_no_parens(self): + namespace = {"active_person": DataDict2(_make_person())} + items = get_completion_items("active_person.gramps_", 1, len("active_person.gramps_"), namespace) + matching = [i for i in items if i["name"] == "gramps_id"] + self.assertEqual( + matching, [{"name": "gramps_id", "complete": "id", "cursor_offset": 0}] + ) class TestRobustness(_MockSaBase): diff --git a/GrampyScript/tests/test_completion_popup.py b/GrampyScript/tests/test_completion_popup.py index bcb788daa..53d238335 100644 --- a/GrampyScript/tests/test_completion_popup.py +++ b/GrampyScript/tests/test_completion_popup.py @@ -101,6 +101,32 @@ def test_accept_with_no_items_just_closes(self): self.assertFalse(controller.is_open()) window.destroy() + def test_accept_no_arg_function_places_cursor_after_parens(self): + controller, buffer, window = _make_controller("peop") + controller.trigger() + names = [item["name"] for item in controller.items] + self.assertIn("people()", names) + controller.selected_index = names.index("people()") + controller.accept() + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "people()") + cursor = buffer.get_iter_at_mark(buffer.get_insert()).get_offset() + self.assertEqual(cursor, len("people()")) + window.destroy() + + def test_accept_function_with_args_places_cursor_between_parens(self): + controller, buffer, window = _make_controller("custom_fil") + controller.trigger() + names = [item["name"] for item in controller.items] + self.assertIn("custom_filter()", names) + controller.selected_index = names.index("custom_filter()") + controller.accept() + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "custom_filter()") + cursor = buffer.get_iter_at_mark(buffer.get_insert()).get_offset() + self.assertEqual(cursor, len("custom_filter(")) + window.destroy() + class TestNavigation(unittest.TestCase): def test_move_selection_clamped(self): From 58bc0a725684016a2f80e7aa328cc6e35451b4dc Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 19:43:05 -0700 Subject: [PATCH 013/156] Auto-insert single-match completions instead of showing a one-row popup Why show a dropdown with nothing to choose between? trigger() now inserts directly when there's exactly one candidate, falling back to the popover only when there's a real choice to make. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/completion_popup.py | 10 +++- GrampyScript/tests/test_completion_popup.py | 52 ++++++++++++++------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/GrampyScript/completion_popup.py b/GrampyScript/completion_popup.py index 9646da588..8ce9c66d1 100644 --- a/GrampyScript/completion_popup.py +++ b/GrampyScript/completion_popup.py @@ -178,8 +178,10 @@ def _compute_items(self): return items def trigger(self): - """Try to open the popover at the cursor. Returns True if it - did (there was something completable to show).""" + """Try to complete at the cursor. Returns True if there was + something completable to show. A single match is inserted + directly instead of opening a popover with one row in it; + multiple matches open the popover as usual.""" if not self._is_completable_context(): return False items = self._compute_items() @@ -188,6 +190,10 @@ def trigger(self): return False self.items = items self.selected_index = 0 + if len(items) == 1: + _LOG.debug("trigger: single match, inserting directly: %s", items[0]["name"]) + self.accept() + return True self._open_popover() return True diff --git a/GrampyScript/tests/test_completion_popup.py b/GrampyScript/tests/test_completion_popup.py index 53d238335..047ee62d2 100644 --- a/GrampyScript/tests/test_completion_popup.py +++ b/GrampyScript/tests/test_completion_popup.py @@ -79,14 +79,26 @@ def test_close_resets_state(self): self.assertEqual(controller.selected_index, 0) window.destroy() + def test_trigger_inserts_directly_when_only_one_match(self): + # "primary_" only matches primary_name among active_person's + # dynamic keys -- with a single candidate there is nothing to + # choose between, so it should be inserted immediately rather + # than opening a one-row popover. + controller, buffer, window = _make_controller("active_person.primary_") + opened = controller.trigger() + self.assertTrue(opened) + self.assertFalse(controller.is_open()) + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "active_person.primary_name") + window.destroy() + class TestAccept(unittest.TestCase): def test_accept_inserts_missing_suffix_only(self): - controller, buffer, window = _make_controller("active_person.primary_") + controller, buffer, window = _make_controller("active_person.") controller.trigger() - # first match should be primary_name (only dynamic key matching) names = [item["name"] for item in controller.items] - self.assertEqual(names, ["primary_name"]) + controller.selected_index = names.index("primary_name") controller.accept() text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) self.assertEqual(text, "active_person.primary_name") @@ -102,12 +114,11 @@ def test_accept_with_no_items_just_closes(self): window.destroy() def test_accept_no_arg_function_places_cursor_after_parens(self): + # "peop" has only one match (people()), so trigger() inserts it + # directly -- exercises the same accept() cursor-placement code + # path as a manual popover selection would. controller, buffer, window = _make_controller("peop") controller.trigger() - names = [item["name"] for item in controller.items] - self.assertIn("people()", names) - controller.selected_index = names.index("people()") - controller.accept() text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) self.assertEqual(text, "people()") cursor = buffer.get_iter_at_mark(buffer.get_insert()).get_offset() @@ -117,10 +128,6 @@ def test_accept_no_arg_function_places_cursor_after_parens(self): def test_accept_function_with_args_places_cursor_between_parens(self): controller, buffer, window = _make_controller("custom_fil") controller.trigger() - names = [item["name"] for item in controller.items] - self.assertIn("custom_filter()", names) - controller.selected_index = names.index("custom_filter()") - controller.accept() text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) self.assertEqual(text, "custom_filter()") cursor = buffer.get_iter_at_mark(buffer.get_insert()).get_offset() @@ -144,17 +151,27 @@ def test_move_selection_clamped(self): class TestOnKeyPress(unittest.TestCase): - def test_tab_opens_then_accepts(self): + def test_tab_completes_directly_for_single_match(self): controller, buffer, window = _make_controller("active_person.primary_") consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) self.assertTrue(consumed) + self.assertFalse(controller.is_open()) + text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) + self.assertEqual(text, "active_person.primary_name") + window.destroy() + + def test_tab_opens_then_accepts_for_multiple_matches(self): + controller, buffer, window = _make_controller("active_person.") + consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) + self.assertTrue(consumed) self.assertTrue(controller.is_open()) consumed = controller.on_key_press(_FakeEvent(Gdk.KEY_Tab)) self.assertTrue(consumed) self.assertFalse(controller.is_open()) text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) - self.assertEqual(text, "active_person.primary_name") + self.assertTrue(text.startswith("active_person.")) + self.assertGreater(len(text), len("active_person.")) window.destroy() def test_tab_falls_through_when_nothing_completable(self): @@ -188,13 +205,16 @@ def test_left_right_close_but_are_not_consumed(self): window.destroy() def test_return_accepts_only_while_open(self): - controller, buffer, window = _make_controller("active_person.primary_") + controller, buffer, window = _make_controller("active_person.") # popover not open: Return must not be swallowed (newline/apply-script bindings) self.assertFalse(controller.on_key_press(_FakeEvent(Gdk.KEY_Return))) controller.trigger() + self.assertTrue(controller.is_open()) self.assertTrue(controller.on_key_press(_FakeEvent(Gdk.KEY_Return))) + self.assertFalse(controller.is_open()) text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) - self.assertEqual(text, "active_person.primary_name") + self.assertTrue(text.startswith("active_person.")) + self.assertGreater(len(text), len("active_person.")) window.destroy() @@ -212,7 +232,7 @@ def test_refresh_narrows_as_more_is_typed(self): window.destroy() def test_refresh_closes_when_context_no_longer_completable(self): - controller, buffer, window = _make_controller("active_person.primary_") + controller, buffer, window = _make_controller("active_person.") controller.trigger() self.assertTrue(controller.is_open()) From 63215112d476d8ebf45df96d88730387cb3bb9d7 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 20:05:53 -0700 Subject: [PATCH 014/156] Give age and back_references proper types in the completion stub age was typed as "object" even though DataDict2.age actually returns a gramps.gen.lib.date.Span (from Date - Date), so jedi had nothing to complete on it. back_references/back_references_recursively had the same "object" placeholder, which is worse than useless since jedi can't iterate a bare object at all -- completions on their loop items returned nothing. Both are now typed precisely: age as Span (imported into the stub preamble), and the back-reference properties as a union of every row type, mirroring the existing selected()/filtered() trick. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/stub_generator.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/GrampyScript/stub_generator.py b/GrampyScript/stub_generator.py index a054ea2c6..66165dcc6 100644 --- a/GrampyScript/stub_generator.py +++ b/GrampyScript/stub_generator.py @@ -102,13 +102,22 @@ "custom_filter": ["name: str", 'namespace: str = "Person"'], } +# back_references(_recursively) can resolve to any primary object type at +# runtime (datadict2.py looks up the handle's own table), so -- same trick as +# TABLE_FUNCTIONS above -- type them as the union of every row type rather +# than "object": jedi merges every union member's attributes, which is more +# useful than no completions at all past a plain "object". +_BACK_REFERENCE_TYPE = 'list[Union[%s]]' % ", ".join( + '"%s"' % name for name in sorted(set(GENERATOR_ROW_TYPES.values())) +) + # DataDict2's computed @property names (datadict2.py), layered onto every # generated type since DataDict2 defines them once for every instance # regardless of the wrapped record's real class. Best-effort types; "object" # is used where the real return type is ambiguous or data-dependent. COMPUTED_PROPERTIES = { "gender": "str", - "age": "object", + "age": "Span", "birth": "Event", "death": "Event", "place": "Place", @@ -130,8 +139,8 @@ "addresses": 'list["Address"]', "lds_ords": 'list["LdsOrdinance"]', "references": 'list["PersonRef"]', - "back_references": "object", - "back_references_recursively": "object", + "back_references": _BACK_REFERENCE_TYPE, + "back_references_recursively": _BACK_REFERENCE_TYPE, "name": "Name", "surname": "Surname", "names": 'list["Name"]', @@ -209,6 +218,7 @@ def render_stub_source( lines = [ "from __future__ import annotations", "from typing import Iterator, Union", + "from gramps.gen.lib.date import Span", "", ] for name in sorted(registry): From d14fd4736ffbf2319a3f54c2b469cced4c3ee042 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 6 Jul 2026 20:46:06 -0700 Subject: [PATCH 015/156] Give each record type its own accurate completion fields COMPUTED_PROPERTIES was a single flat dict layered onto every schema class, including nested structural types (Name, Attribute, ...), so the editor offered fields like father/spouse/gender/age everywhere -- even where the underlying DataDict2 property would raise (gender on a non-Person) or silently do nothing. It's now name -> (type, valid root types), and build_registry() only attaches a property to the root record types it's actually valid on. `reference` moves out entirely, onto the nested *Ref wrapper types it actually belongs to. Also fixes two real datadict2.py bugs the audit turned up: surname/name used unguarded self["surname"]/self["name"], raising KeyError on any class without that field; reference always called get_raw_person_data regardless of which *Ref type it was wrapping. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 29 +++++- GrampyScript/stub_generator.py | 108 ++++++++++++++-------- GrampyScript/tests/test_datadict2.py | 44 +++++++++ GrampyScript/tests/test_stub_generator.py | 22 ++++- 4 files changed, 154 insertions(+), 49 deletions(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index a969311f5..34ad6faa8 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -31,6 +31,17 @@ from gramps.gen.config import config NoneType = type(None) + +# *Ref._class -> the table its `ref` handle points into, for the `reference` +# property below. +REFERENCE_TABLES = { + "ChildRef": "Person", + "EventRef": "Event", + "MediaRef": "Media", + "PersonRef": "Person", + "PlaceRef": "Place", + "RepoRef": "Repository", +} invalid_date_format = config.get("preferences.invalid-date-format") age_precision = config.get("preferences.age-display-precision") age_after_death = config.get("preferences.age-after-death") @@ -260,7 +271,17 @@ def events(self): @property def reference(self): - return DataDict2(sa.dbase.get_raw_person_data(self.ref), callback=self.callback) + # self is one of the *Ref wrapper types (PersonRef, EventRef, ...); + # `ref` is a handle into whichever table its own _class points at, + # not always Person. + table = REFERENCE_TABLES.get(self["_class"]) + if table is None: + return NoneData() + getter = sa.dbase.method("get_raw_%s_data", table) + data = getter(self.ref) if getter else None + if data is None: + return NoneData() + return DataDict2(data, callback=self.callback) @property def attributes(self): @@ -298,15 +319,13 @@ def back_references_recursively(self): def name(self): if self["_class"] == "Person": return self.primary_name - else: - return self["name"] + return self["name"] if "name" in self else NoneData() @property def surname(self): if self["_class"] == "Person": return self.primary_name.surname_list[0] - else: - return self["surname"] + return self["surname"] if "surname" in self else NoneData() @property def names(self): diff --git a/GrampyScript/stub_generator.py b/GrampyScript/stub_generator.py index 66165dcc6..a59fce531 100644 --- a/GrampyScript/stub_generator.py +++ b/GrampyScript/stub_generator.py @@ -107,43 +107,61 @@ # TABLE_FUNCTIONS above -- type them as the union of every row type rather # than "object": jedi merges every union member's attributes, which is more # useful than no completions at all past a plain "object". -_BACK_REFERENCE_TYPE = 'list[Union[%s]]' % ", ".join( - '"%s"' % name for name in sorted(set(GENERATOR_ROW_TYPES.values())) -) +_ALL_ROOT = set(GENERATOR_ROW_TYPES.values()) +_BACK_REFERENCE_TYPE = 'list[Union[%s]]' % ", ".join('"%s"' % name for name in sorted(_ALL_ROOT)) + +_PERSON = {"Person"} +_PERSON_FAMILY = {"Person", "Family"} -# DataDict2's computed @property names (datadict2.py), layered onto every -# generated type since DataDict2 defines them once for every instance -# regardless of the wrapped record's real class. Best-effort types; "object" -# is used where the real return type is ambiguous or data-dependent. +# DataDict2's computed @property names (datadict2.py): type_annotation plus +# which root row types the property is actually valid on, derived from what +# each property's own code requires -- a SimpleAccess call that asserts its +# argument type (e.g. sa.gender, sa.spouse: Person only), or a raw dict field +# that only some schemas have (e.g. `place` needs Event.place, `source` needs +# Citation.source_handle). Layering a property onto a type it doesn't apply +# to would offer a completion that's either silently useless or -- like the +# old `gender`-on-non-Person -- raises at runtime. COMPUTED_PROPERTIES = { - "gender": "str", - "age": "Span", - "birth": "Event", - "death": "Event", - "place": "Place", - "parents": 'list["Person"]', - "father": "Person", - "mother": "Person", - "spouse": "Person", - "source": "Source", - "families": 'list["Family"]', - "parent_families": 'list["Family"]', - "children": 'list["Person"]', - "notes": 'list["Note"]', - "tags": 'list["Tag"]', - "citations": 'list["Citation"]', - "media": 'list["MediaRef"]', - "events": 'list["Event"]', - "reference": "Person", - "attributes": 'list["Attribute"]', - "addresses": 'list["Address"]', - "lds_ords": 'list["LdsOrdinance"]', - "references": 'list["PersonRef"]', - "back_references": _BACK_REFERENCE_TYPE, - "back_references_recursively": _BACK_REFERENCE_TYPE, - "name": "Name", - "surname": "Surname", - "names": 'list["Name"]', + "gender": ("str", _PERSON), + "age": ("Span", _PERSON), + "birth": ("Event", _PERSON), + "death": ("Event", _PERSON), + "place": ("Place", {"Event"}), + "parents": ('list["Person"]', _PERSON_FAMILY), + "father": ("Person", _PERSON_FAMILY), + "mother": ("Person", _PERSON_FAMILY), + "spouse": ("Person", _PERSON), + "source": ("Source", {"Citation"}), + "families": ('list["Family"]', _PERSON), + "parent_families": ('list["Family"]', _PERSON), + "children": ('list["Person"]', _PERSON_FAMILY), + "notes": ('list["Note"]', _ALL_ROOT - {"Note"}), + "tags": ('list["Tag"]', _ALL_ROOT), + "citations": ('list["Citation"]', {"Person", "Family", "Event", "Place", "Media"}), + "media": ('list["MediaRef"]', {"Person", "Family", "Event", "Place", "Source", "Citation"}), + "events": ('list["Event"]', _PERSON_FAMILY), + "attributes": ('list["Attribute"]', {"Person", "Family", "Event", "Source", "Citation", "Media"}), + "addresses": ('list["Address"]', {"Person", "Repository"}), + "lds_ords": ('list["LdsOrdinance"]', _PERSON_FAMILY), + "references": ('list["PersonRef"]', _PERSON), + "back_references": (_BACK_REFERENCE_TYPE, _ALL_ROOT), + "back_references_recursively": (_BACK_REFERENCE_TYPE, _ALL_ROOT), + "name": ("Name", _PERSON), + "surname": ("Surname", _PERSON), + "names": ('list["Name"]', _PERSON), +} + +# `reference` (datadict2.py) isn't valid on any root row type -- it reads +# `self.ref`, a handle that exists only on the nested *Ref wrapper types +# (mirrors datadict2.REFERENCE_TABLES). Sanitized schema class name -> the +# row type its `ref` handle points into. +REFERENCE_TARGET_TYPES = { + "ChildReference": "Person", + "EventReference": "Event", + "MediaRef": "Media", + "PersonRef": "Person", + "PlaceRef": "Place", + "RepositoryRef": "Repository", } _SCALAR_TYPES = {"string": "str", "integer": "int", "boolean": "bool", "number": "float"} @@ -190,15 +208,25 @@ def _walk(schema, registry): def build_registry(root_classes=ROOT_CLASSES): """ Return {sanitized_class_name: {field_name: type_annotation}} for every - type reachable from `root_classes` via Gramps' own get_schema(), with - DataDict2's computed properties layered on top of each (matching real - attribute lookup order: properties shadow raw dict keys). + type reachable from `root_classes` via Gramps' own get_schema(). Each + entry in COMPUTED_PROPERTIES is layered only onto the root row types it + lists as valid (matching real attribute lookup order: properties shadow + raw dict keys) -- nested structural types (Name, Attribute, ...) get + schema fields only. `reference` is layered separately, onto the nested + *Ref types listed in REFERENCE_TARGET_TYPES, since that's what it's + actually valid on. """ registry = {} for cls in root_classes: _walk(cls.get_schema(), registry) - for fields in registry.values(): - fields.update(COMPUTED_PROPERTIES) + for class_name, fields in registry.items(): + if class_name in _ALL_ROOT: + for prop_name, (type_, valid_for) in COMPUTED_PROPERTIES.items(): + if class_name in valid_for: + fields[prop_name] = type_ + target = REFERENCE_TARGET_TYPES.get(class_name) + if target is not None: + fields["reference"] = target return registry diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index d7ab9e523..6dd6ceb1d 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -47,6 +47,12 @@ def setUp(self): db.get_event_from_handle.return_value = None db.get_place_from_handle.return_value = None db.get_source_from_handle.return_value = None + # Mirror DbGeneric.method()'s real dispatch (getattr on a formatted, + # lowercased method name), since a bare MagicMock doesn't do this. + db.method.side_effect = lambda fmt, *args: getattr( + db, fmt % tuple(a.lower() for a in args), None + ) + self.db = db sa = SimpleAccess(db) set_sa(sa) @@ -163,6 +169,44 @@ def test_family_gramps_id(self): self.assertEqual(dd["_class"], "Family") +# --------------------------------------------------------------------------- +# DataDict2 — surname/name/reference on non-Person and *Ref wrappers +# --------------------------------------------------------------------------- + + +class TestDataDict2NonPersonProperties(_MockSaBase): + def test_surname_is_none_data_for_non_person(self): + # Regression: used to raise KeyError via self["surname"], since no + # schema has a top-level "surname" field. + dd = DataDict2(_make_family()) + self.assertIsInstance(dd.surname, NoneData) + + def test_name_is_none_data_when_field_absent(self): + # Regression: used to raise KeyError via self["name"] for classes + # (like Family) with no "name" schema field. + dd = DataDict2(_make_family()) + self.assertIsInstance(dd.name, NoneData) + + def test_reference_dispatches_by_class(self): + # Regression: used to always call get_raw_person_data regardless of + # which *Ref type was wrapped. + from gramps.gen.lib import PersonRef + from gramps.gen.lib.json_utils import object_to_dict + + ref = PersonRef() + ref.set_reference_handle("HANDLE1") + self.db.get_raw_person_data.return_value = object_to_dict( + _make_person(gramps_id="I9999") + ) + dd = DataDict2(ref) + self.assertEqual(dd.reference.gramps_id, "I9999") + self.db.get_raw_person_data.assert_called_with("HANDLE1") + + def test_reference_none_data_for_unmapped_class(self): + dd = DataDict2(_make_family()) + self.assertIsInstance(dd.reference, NoneData) + + # --------------------------------------------------------------------------- # DataDict2 — null-safe chaining # --------------------------------------------------------------------------- diff --git a/GrampyScript/tests/test_stub_generator.py b/GrampyScript/tests/test_stub_generator.py index 3467b6215..7bf0fc0f2 100644 --- a/GrampyScript/tests/test_stub_generator.py +++ b/GrampyScript/tests/test_stub_generator.py @@ -41,12 +41,26 @@ def test_nested_list_field_is_typed(self): fields = self.registry["Person"] self.assertEqual(fields["address_list"], 'list["Address"]') - def test_computed_properties_layered_on_every_type(self): - # DataDict2's @property names apply to every wrapped record, not - # just Person, since it is the same class for every nested value. - for name in ["Person", "Family", "Name"]: + def test_computed_properties_layered_on_matching_root_types(self): + # `father` is valid on Person and Family (sa.father accepts both). + for name in ["Person", "Family"]: self.assertEqual(self.registry[name]["father"], "Person") + def test_computed_properties_not_layered_on_mismatched_types(self): + # `father` shouldn't leak onto nested structural types (Name is + # reached only by walking Person.primary_name, not a root row type), + # nor onto root types the underlying SimpleAccess call rejects. + self.assertNotIn("father", self.registry["Name"]) + self.assertNotIn("spouse", self.registry["Event"]) + self.assertNotIn("gender", self.registry["Family"]) + + def test_reference_layered_only_on_ref_types(self): + # `reference` reads a `ref` handle that only *Ref wrapper types + # have -- it shouldn't appear on the root row types themselves. + self.assertEqual(self.registry["PersonRef"]["reference"], "Person") + self.assertEqual(self.registry["EventReference"]["reference"], "Event") + self.assertNotIn("reference", self.registry["Person"]) + def test_computed_property_overrides_raw_field(self): # `gender` is both a raw int field and a DataDict2 @property; # the property wins at real attribute-lookup time. From f2ad0f52ed65e7674228e2769dc0485ae28e4de0 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 07:10:33 -0700 Subject: [PATCH 016/156] Make completions consistent for callables and drop classes entirely get_completions() returned bare function names (e.g. "people", "as_age") while get_completion_items() appended "()" to the same completions -- inconsistent, and a bare name reads as a field rather than a callable. Both now share a _display_name() helper so callables agree everywhere. Also exclude jedi type "class" completions altogether: the stub preamble injects scaffold classes (Person, Family, ...) purely for static analysis, and they aren't bound to anything in the namespace a script actually executes in, so offering them as completions would suggest names that raise NameError if accepted. Builtin classes (list, dict, ...) are dropped too, since the DSL has no use for instantiating classes directly. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/completion.py | 37 ++++++++++++++------- GrampyScript/tests/test_completion.py | 46 +++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/GrampyScript/completion.py b/GrampyScript/completion.py index 54439d946..d1b4abe40 100644 --- a/GrampyScript/completion.py +++ b/GrampyScript/completion.py @@ -49,14 +49,32 @@ def _get_stub_preamble(): def _complete(source, line, column, namespace): """Shared jedi call underlying both get_completions() and - get_completion_items(); returns raw jedi Completion objects.""" + get_completion_items(); returns raw jedi Completion objects, excluding + classes (jedi type "class"). The DSL has no use for instantiating + classes directly, and the stub preamble's own scaffold classes + (Person, Family, ...) would otherwise leak into the list -- they exist + only for jedi's static analysis and aren't bound to anything in the + namespace a script actually runs in, so offering them as completions + would suggest names that raise NameError if accepted.""" preamble = _get_stub_preamble() full_source = preamble + source interpreter = jedi.Interpreter(full_source, [namespace]) try: - return interpreter.complete(line + preamble.count("\n"), column) + completions = interpreter.complete(line + preamble.count("\n"), column) except Exception: return [] + return [completion for completion in completions if completion.type != "class"] + + +def _display_name(completion): + """Completion name for display: function/method completions (jedi type + "function", e.g. `people`, `print`) get "()" appended, so both + get_completions() and get_completion_items() consistently show a + callable as callable rather than as a bare, field-like name.""" + name = completion.name + if completion.type == "function": + name += "()" + return name def get_completions(source, line, column, namespace): @@ -74,7 +92,7 @@ def get_completions(source, line, column, namespace): to runtime introspection (dir()/getattr()) for anything it can't statically analyze. """ - return [completion.name for completion in _complete(source, line, column, namespace)] + return [_display_name(completion) for completion in _complete(source, line, column, namespace)] def _takes_arguments(completion): @@ -100,20 +118,17 @@ def get_completion_items(source, line, column, namespace): "rt"), so callers can insert it directly without recomputing/re-typing the already-typed prefix. - Function/method completions (jedi type "function", e.g. `people`, - `families`) get "()" appended to both `name` (so the popup reads - "people()") and `complete`; `cursor_offset` is then 1 for functions - that take arguments, landing the cursor between the parens ready to - type them, or 0 for no-argument functions, landing it after the - closing paren. + Function/method completions also get "()" appended to `complete`; + `cursor_offset` is then 1 for functions that take arguments, landing + the cursor between the parens ready to type them, or 0 for + no-argument functions, landing it after the closing paren. """ items = [] for completion in _complete(source, line, column, namespace): - name = completion.name + name = _display_name(completion) complete = completion.complete cursor_offset = 0 if completion.type == "function": - name += "()" complete += "()" if _takes_arguments(completion): cursor_offset = 1 diff --git a/GrampyScript/tests/test_completion.py b/GrampyScript/tests/test_completion.py index 12ce86ddc..d76cfd64f 100644 --- a/GrampyScript/tests/test_completion.py +++ b/GrampyScript/tests/test_completion.py @@ -46,8 +46,10 @@ def _complete(self, source, namespace): class TestBareWordCompletion(_MockSaBase): def test_completes_python_builtins(self): + # print is a function, so it gets "()" appended like any other + # callable completion -- see TestCompletionItems below. names = self._complete("pri", {}) - self.assertIn("print", names) + self.assertIn("print()", names) def test_completes_namespace_variable(self): names = self._complete("active_per", {"active_person": DataDict2(_make_person())}) @@ -111,6 +113,46 @@ def test_distinguishes_row_type_by_generator(self): self.assertNotIn("primary_name", names) +class TestGetCompletionsFunctionParens(_MockSaBase): + """ + Regression: get_completions() used to return bare function names + (e.g. "people", "format") while get_completion_items() appended "()" + to the same completions -- inconsistent and misleading, since a bare + name reads as a field rather than a callable. Both now agree. + """ + + def test_bare_function_completion_gets_parens(self): + names = self._complete("peop", {}) + self.assertIn("people()", names) + self.assertNotIn("people", names) + + def test_non_function_completion_has_no_parens(self): + namespace = {"active_person": DataDict2(_make_person())} + names = self._complete("active_person.gramps_", namespace) + self.assertIn("gramps_id", names) + + +class TestClassCompletionsExcluded(_MockSaBase): + """ + Classes (jedi type "class") are excluded entirely, not just left + without "()". The stub preamble injects scaffold classes (Person, + Family, ...) purely for jedi's static analysis -- they aren't bound to + anything in the namespace a script actually executes in, so offering + them as completions would suggest names that raise NameError if + accepted. Builtin classes (list, dict, ...) are excluded too, since + the DSL has no use for instantiating classes directly. + """ + + def test_stub_scaffold_class_not_offered(self): + names = self._complete("Perso", {}) + self.assertNotIn("Person", names) + self.assertNotIn("PersonRef", names) + + def test_builtin_class_not_offered(self): + names = self._complete("li", {}) + self.assertNotIn("list", names) + + class TestCompletionItems(_MockSaBase): """get_completion_items() is get_completions() plus the jedi `.complete` suffix, used by the editor to insert just the missing @@ -160,7 +202,7 @@ def test_empty_source_does_not_raise(self): # Completing on an empty buffer legitimately lists every builtin # in scope; the point of this test is only that it doesn't raise. names = self._complete("", {}) - self.assertIn("print", names) + self.assertIn("print()", names) def test_incomplete_code_does_not_raise(self): # Mid-typing code is often syntactically invalid; must not crash. From b3d409a3f28eea29b510a03025af0f17149ec54f Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 07:21:26 -0700 Subject: [PATCH 017/156] Hint at Tab completion in the status bar and fix New not resetting filename The status message duplicated the filename already shown by filename_label, so drop those redundant messages and use the freed-up space to surface the Tab-completion shortcut. Also fix New leaving the old filename label in place, which caused Save to silently overwrite the previous file instead of prompting Save As. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index ece0f7035..896adb2ec 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -311,9 +311,7 @@ def init(self): self.update_filename_label() if os.path.exists(self.last_filename): self.ebuf.set_text(open(self.last_filename).read()) - self.statusmsg.set_text("Loaded %r" % self.last_filename) else: - self.statusmsg.set_text("Current filename: %r" % self.last_filename) self.ebuf.set_text( """# This is a sample script @@ -476,7 +474,7 @@ def build_gui(self): provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) - self.statusmsg = Gtk.Label(_("Ready...")) + self.statusmsg = Gtk.Label(_("Ready... (Tab for completions)")) self.statusmsg.set_xalign(0) # 0.0 for left, 0.5 for center, 1.0 for right # Some status messages embed the full path of the current file # (e.g. "Loaded '/home/.../scripts/some_script.gram.py'"), which @@ -540,7 +538,9 @@ def new_script(self, widget): def _do_new_script(self): self.ebuf.set_text("") self.ebuf.set_modified(False) - self.statusmsg.set_text("Ready...") + self.last_filename = "" + self.update_filename_label() + self.statusmsg.set_text(_("Ready... (Tab for completions)")) def open_script(self, widget): # type: (Gtk.Widget) -> None @@ -568,7 +568,6 @@ def _do_open_script(self): config.set("defaults.last_filename", filename) config.save() self.update_filename_label() - self.statusmsg.set_text("Loaded %r" % self.last_filename) break choose_file_dialog.destroy() @@ -580,7 +579,7 @@ def save_script(self, widget): with open(self.last_filename, "w") as fp: fp.write(self.get_text()) self.ebuf.set_modified(False) - self.statusmsg.set_text("Saved %r" % self.last_filename) + self.statusmsg.set_text("Saved") def save_as_script(self, widget): choose_file_dialog = ScriptSaveFileChooserDialog(self.uistate) @@ -608,7 +607,7 @@ def save_as_script(self, widget): config.set("defaults.last_filename", filename) config.save() self.update_filename_label() - self.statusmsg.set_text("Saved as %r (now current)" % self.last_filename) + self.statusmsg.set_text("Saved as (now current)") break choose_file_dialog.destroy() From 3a35c8fa3dc3b068977e0544abe7e527f2cc871b Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 07:27:16 -0700 Subject: [PATCH 018/156] Add completion for columns() and other void DSL functions columns, begin_changes, end_changes, delete, row, and chart are all top-level DSL callables bound as local closures inside execute_code(), so jedi never saw them since they weren't part of the completion stub or namespace. Adds a VOID_FUNCTIONS entry in stub_generator.py that renders their signatures as "-> None" completions. --- GrampyScript/stub_generator.py | 19 ++++++++++ GrampyScript/tests/test_stub_generator.py | 44 ++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/GrampyScript/stub_generator.py b/GrampyScript/stub_generator.py index a59fce531..5b7230744 100644 --- a/GrampyScript/stub_generator.py +++ b/GrampyScript/stub_generator.py @@ -102,6 +102,22 @@ "custom_filter": ["name: str", 'namespace: str = "Person"'], } +# Other top-level DSL callables bound in execute_code() (GrampyScript.py): +# real functions/bound methods with side effects (printing a row, opening/ +# closing a transaction, drawing a chart, deleting a record) rather than +# something whose return value ever gets chained. None of these need row-type +# inference, just enough of a signature for jedi to offer them as completions +# and to know their parameters -- hence "-> None" rather than being folded +# into TABLE_FUNCTIONS. +VOID_FUNCTIONS = { + "row": ["*args"], + "columns": ["*column_names"], + "begin_changes": ['message: str = ""'], + "end_changes": [], + "delete": ["obj"], + "chart": ["type", "data", "count: int = 20", "**kwargs"], +} + # back_references(_recursively) can resolve to any primary object type at # runtime (datadict2.py looks up the handle's own table), so -- same trick as # TABLE_FUNCTIONS above -- type them as the union of every row type rather @@ -235,6 +251,7 @@ def render_stub_source( generator_row_types=GENERATOR_ROW_TYPES, table_functions=TABLE_FUNCTIONS, active_variables=ACTIVE_VARIABLES, + void_functions=VOID_FUNCTIONS, ): """ Render `registry` plus DSL generator function signatures and active_* @@ -266,6 +283,8 @@ def render_stub_source( lines.append( "def %s(%s) -> Iterator[%s]: ..." % (func_name, ", ".join(params), row_union) ) + for func_name, params in void_functions.items(): + lines.append("def %s(%s) -> None: ..." % (func_name, ", ".join(params))) lines.append("") for var_name, row_type in active_variables.items(): lines.append("%s: %s" % (var_name, row_type)) diff --git a/GrampyScript/tests/test_stub_generator.py b/GrampyScript/tests/test_stub_generator.py index 7bf0fc0f2..55121c48a 100644 --- a/GrampyScript/tests/test_stub_generator.py +++ b/GrampyScript/tests/test_stub_generator.py @@ -10,7 +10,13 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from stub_generator import ACTIVE_VARIABLES, GENERATOR_ROW_TYPES, build_registry, render_stub_source +from stub_generator import ( + ACTIVE_VARIABLES, + GENERATOR_ROW_TYPES, + VOID_FUNCTIONS, + build_registry, + render_stub_source, +) from completion import get_completions @@ -112,6 +118,21 @@ def test_no_active_variables_when_omitted(self): source = render_stub_source(build_registry(), active_variables={}) self.assertNotIn("active_person:", source) + def test_void_functions_present(self): + source = render_stub_source(build_registry()) + self.assertIn("def columns(*column_names) -> None: ...", source) + self.assertIn('def begin_changes(message: str = "") -> None: ...', source) + self.assertIn("def end_changes() -> None: ...", source) + self.assertIn("def delete(obj) -> None: ...", source) + self.assertIn("def row(*args) -> None: ...", source) + self.assertIn( + "def chart(type, data, count: int = 20, **kwargs) -> None: ...", source + ) + + def test_no_void_functions_when_omitted(self): + source = render_stub_source(build_registry(), void_functions={}) + self.assertNotIn("def columns", source) + class TestActiveVariableCompletion(unittest.TestCase): """ @@ -175,5 +196,26 @@ def test_custom_filter_offers_real_fields_with_explicit_namespace(self): self.assertIn("father_handle", names) +class TestVoidFunctionCompletion(unittest.TestCase): + """ + columns()/begin_changes()/end_changes()/delete()/row()/chart() are void + DSL functions (VOID_FUNCTIONS) -- they don't need row-type inference, + just a signature so jedi offers them as completions at all. Before + these were added to the stub, jedi had no way to know these names exist + since they're bound as local closures inside execute_code(), never + passed through the completion namespace. + """ + + def _complete(self, source): + lines = source.splitlines() + return get_completions(source, len(lines), len(lines[-1]), {}) + + def test_completes_void_function_names(self): + for name in VOID_FUNCTIONS: + with self.subTest(name=name): + names = self._complete(name[:-1]) + self.assertIn(name + "()", names) + + if __name__ == "__main__": unittest.main() From bfe1a7554dafa408330ba9dcee33b188f1b00d21 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 10:00:05 -0700 Subject: [PATCH 019/156] Close files explicitly instead of relying on GC open(path).read() without a context manager leaves the file handle open until garbage collected, which triggers ResourceWarning under python -m unittest. Use with-blocks in the four spots that did this. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/tests/test_script_descriptions.py | 6 ++++-- GrampyScript/update_script_descriptions.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/GrampyScript/tests/test_script_descriptions.py b/GrampyScript/tests/test_script_descriptions.py index a0df5b6b2..4785cebd1 100644 --- a/GrampyScript/tests/test_script_descriptions.py +++ b/GrampyScript/tests/test_script_descriptions.py @@ -56,7 +56,8 @@ def test_regenerating_produces_no_changes(self): self.assertEqual(errors, []) header = _load_header(DESCRIPTIONS_PATH) regenerated = build_source(entries, header) - on_disk = open(DESCRIPTIONS_PATH, encoding="utf-8").read() + with open(DESCRIPTIONS_PATH, encoding="utf-8") as f: + on_disk = f.read() self.assertEqual( regenerated, on_disk, @@ -69,7 +70,8 @@ class TestScriptsAreValidPython(unittest.TestCase): def test_all_scripts_parse(self): for path in glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py")): with self.subTest(path=path): - ast.parse(open(path).read()) + with open(path) as f: + ast.parse(f.read()) if __name__ == "__main__": diff --git a/GrampyScript/update_script_descriptions.py b/GrampyScript/update_script_descriptions.py index d8f508362..8ccd9e351 100644 --- a/GrampyScript/update_script_descriptions.py +++ b/GrampyScript/update_script_descriptions.py @@ -55,7 +55,8 @@ def _load_header(path): """Return the file text up through the "SCRIPT_DESCRIPTIONS = {" line.""" - source = open(path, encoding="utf-8").read() + with open(path, encoding="utf-8") as f: + source = f.read() tree = ast.parse(source) lines = source.splitlines(keepends=True) @@ -103,7 +104,8 @@ def collect_entries(): errors = [] for path in sorted(glob.glob(os.path.join(SCRIPTS_DIR, "*.gram.py"))): filename = os.path.basename(path) - source = open(path, encoding="utf-8").read() + with open(path, encoding="utf-8") as f: + source = f.read() title = extract_header_comment(source) description = ast.get_docstring(ast.parse(source), clean=True) if description: From a01627d1ee9273e2f76ec6821783935ebb7b30fa Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 18:24:00 -0700 Subject: [PATCH 020/156] Fix set_*() on nested DataDict2 wrappers silently discarding changes A nested wrapper's _object was rebuilt via data_to_object() from just its own dict slice, disconnected from the real object tree. Calling a set_*() method (e.g. surname.set_origintype()) mutated that throwaway clone, then the commit step re-serialized the untouched real object, so the change never reached the database. Now nested wrappers resolve _object by walking the root's real object via self.path, so set_*() calls (and attribute assignment) mutate the actual object that gets persisted. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 30 ++++++++++++++------- GrampyScript/tests/test_datadict2.py | 40 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index 34ad6faa8..a190272d2 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -331,19 +331,23 @@ def surname(self): def names(self): return DataList2([self.primary_name] + [self.alternate_names]) + def _real_object(self): + """Walk from the root's real object down self.path to find the + actual (not reconstructed) object that this wrapper represents.""" + obj = self.root._object + for part in self.path: + if isinstance(part, int): + obj = obj[part] + else: + obj = getattr(obj, part) + return obj + def __setattr__(self, attr, value): if attr in ["root", "path", "callback"]: return super().__setattr__(attr, value) else: - # Follow the path: - obj = self.root._object - for part in self.path: - if isinstance(part, int): - obj = obj[part] - else: - obj = getattr(obj, part) # Set it in the real _object: - setattr(obj, attr, value) + setattr(self._real_object(), attr, value) # Update the top-level dict: self.root.update(object_to_dict(self.root._object)) # Call the callback @@ -362,7 +366,15 @@ def __dir__(self): def __getattr__(self, key): if key == "_object": if "_object" not in self: - self["_object"] = data_to_object(self) + # A nested wrapper (non-empty path) must resolve to the + # actual sub-object inside the root's real object tree, + # not a standalone copy reconstructed from its own dict + # slice -- otherwise set_*() calls below mutate a clone + # that is discarded instead of the real, committed object. + if self.path: + self["_object"] = self._real_object() + else: + self["_object"] = data_to_object(self) return self["_object"] elif key.startswith("_"): raise AttributeError( diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index 6dd6ceb1d..171c10cf7 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -266,5 +266,45 @@ def test_empty_list(self): self.assertEqual(list(dl), []) +# --------------------------------------------------------------------------- +# DataDict2 — mutation (attribute assignment and set_*() methods) +# --------------------------------------------------------------------------- + +class TestDataDict2Mutation(_MockSaBase): + def test_top_level_attribute_assignment(self): + dd = DataDict2(_make_person(gramps_id="I0001")) + dd.gramps_id = "I9999" + self.assertEqual(dd._object.get_gramps_id(), "I9999") + self.assertEqual(dd.gramps_id, "I9999") + + def test_nested_attribute_assignment_updates_real_object(self): + # Regression: assigning through a nested wrapper (primary_name is + # not the root) must mutate the real Person's Name object, not a + # disconnected copy. + dd = DataDict2(_make_person(first="John")) + dd.primary_name.first_name = "Zoe" + self.assertEqual(dd._object.get_primary_name().get_first_name(), "Zoe") + self.assertEqual(dd.primary_name.first_name, "Zoe") + + def test_nested_set_method_updates_real_object(self): + # Regression: calling a set_*() method on a nested wrapper (e.g. a + # Surname inside primary_name.surname_list) used to run against a + # standalone object rebuilt from that wrapper's own dict slice, so + # the mutation never reached the real Person and was lost on commit. + dd = DataDict2(_make_person(surname="Smith")) + surname = dd.primary_name.surname_list[0] + surname.set_surname("Jones") + real_surname = dd._object.get_primary_name().get_surname_list()[0] + self.assertEqual(real_surname.get_surname(), "Jones") + self.assertEqual(dd.primary_name.surname_list[0].surname, "Jones") + + def test_nested_set_method_calls_callback_with_root(self): + callback = MagicMock() + dd = DataDict2(_make_person(surname="Smith"), callback=callback) + surname = dd.primary_name.surname_list[0] + surname.set_surname("Jones") + callback.assert_called_once_with("set", dd) + + if __name__ == "__main__": unittest.main() From 705a9f6cedbbab6f1fca59d60bd3edda0dee9c88 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 18:29:22 -0700 Subject: [PATCH 021/156] Fix DataList2 re-wrapping already-wrapped items and corrupting root/path [dd.primary_name] + dd.alternate_names goes through DataList2.__radd__, producing a DataList2 whose elements are already DataDict2/DataList2 instances. __getitem__ unconditionally re-wrapped dict/list values, and since those wrapper classes subclass dict/list, it re-wrapped already- wrapped items too -- discarding their real root/path and substituting this list's own (often None, defaulting to self) root. That produced a DataDict2 whose root was itself but whose path was non-empty, an inconsistent state that made attribute assignment recurse forever trying to resolve self.root._object. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 9 ++++++++- GrampyScript/tests/test_datadict2.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index a190272d2..8051c8c23 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -436,7 +436,14 @@ def __getitem__(self, position, root=None, path=""): value = super().__getitem__(position) except Exception: return NoneData() - if isinstance(value, dict): + # Items can already be fully-wrapped (e.g. after `+`/`__radd__` + # concatenates lists whose elements are DataDict2/DataList2). Since + # both subclass dict/list, re-wrapping them here would discard their + # real root/path and replace it with this list's (often unrelated) + # root, corrupting later attribute assignment. + if isinstance(value, (DataDict2, DataList2)): + return value + elif isinstance(value, dict): return DataDict2(value, root=self.root, path=self.path + [position]) elif isinstance(value, list): return DataList2(value, root=self.root, path=self.path + [position]) diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index 171c10cf7..17c43b842 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -305,6 +305,25 @@ def test_nested_set_method_calls_callback_with_root(self): surname.set_surname("Jones") callback.assert_called_once_with("set", dd) + def test_concatenated_name_list_assignment_updates_real_object(self): + # Regression: `[dd.primary_name] + dd.alternate_names` (the pattern + # used to loop over all of a person's names) goes through + # DataList2.__radd__, which builds a plain list of already-wrapped + # DataDict2 items and re-wraps it in a new DataList2. Iterating that + # outer DataList2 used to re-wrap each *already-wrapped* item via + # __getitem__, discarding its real root/path and replacing it with + # root=self (since the outer list has root=None), producing a + # DataDict2 whose root is itself but whose path is non-empty -- + # an inconsistent state that made attribute assignment recurse + # into itself trying to resolve `self.root._object`. + dd = DataDict2(_make_person(surname="Smith")) + for name in [dd.primary_name] + dd.alternate_names: + self.assertIs(name.root, dd) + for surname in name.surname_list: + surname.set_surname("Jones") + real = dd._object.get_primary_name().get_surname_list()[0] + self.assertEqual(real.get_surname(), "Jones") + if __name__ == "__main__": unittest.main() From e7a0bcedbe10ec4f0f939cdebc7585409214e202 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 18:35:52 -0700 Subject: [PATCH 022/156] Fix .string on GrampsType-based fields returning raw custom-text, not the label The raw serialized "string" field of a GrampsType value (NameOriginType, NameType, EventType, ...) is only the *custom*-type override text -- it is always "" for predefined values like PATRILINEAL. Since DataDict2's generic dict-key lookup returned that raw field directly, `.string` looked empty even after setting a real origin type. Add a `string` property that, when the wrapped value is a GrampsType, returns the actual computed label (str(the_type)) instead. Falls back to normal attribute lookup for anything without a "string" field, so unrelated objects are unaffected. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 17 +++++++++++++++- GrampyScript/tests/test_datadict2.py | 29 +++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index 8051c8c23..e9d735334 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -27,7 +27,7 @@ from __future__ import annotations from gramps.gen.lib.json_utils import data_to_object, object_to_dict -from gramps.gen.lib import PrimaryObject +from gramps.gen.lib import PrimaryObject, GrampsType from gramps.gen.config import config NoneType = type(None) @@ -331,6 +331,21 @@ def surname(self): def names(self): return DataList2([self.primary_name] + [self.alternate_names]) + @property + def string(self): + # The raw "string" field only holds the *custom*-type override text + # for GrampsType-based values (NameOriginType, NameType, EventType, + # ...); for predefined values (the common case) it is always "". + # Route to the real object's `.string` property instead, which + # computes the actual (translated) label. Raising AttributeError + # for anything without a "string" field falls back to the normal + # __getattr__ lookup, so this doesn't change behavior elsewhere. + if "string" not in self: + raise AttributeError("string") + if isinstance(self._object, GrampsType): + return str(self._object) + return self["string"] + def _real_object(self): """Walk from the root's real object down self.path to find the actual (not reconstructed) object that this wrapper represents.""" diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index 17c43b842..00fa2f066 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -11,7 +11,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from gramps.gen.lib import Person, Name, Surname, Family +from gramps.gen.lib import Person, Name, Surname, Family, NameOriginType from gramps.gen.simple import SimpleAccess from datadict2 import DataDict2, DataList2, NoneData, set_sa @@ -325,5 +325,32 @@ def test_concatenated_name_list_assignment_updates_real_object(self): self.assertEqual(real.get_surname(), "Jones") +# --------------------------------------------------------------------------- +# DataDict2 — .string for GrampsType-based fields (NameOriginType, ...) +# --------------------------------------------------------------------------- + +class TestDataDict2TypeString(_MockSaBase): + def test_origintype_string_reflects_predefined_value(self): + # Regression: the raw "string" field only holds the *custom*-type + # override text, which is always "" for predefined values like + # PATRILINEAL. `.string` must return the real, computed label + # instead of that raw (and misleadingly empty) field. + dd = DataDict2(_make_person(surname="Smith")) + surname = dd.primary_name.surname_list[0] + surname.set_origintype(NameOriginType.PATRILINEAL) + self.assertEqual(dd.primary_name.surname_list[0].origintype.string, "Patrilineal") + + def test_origintype_string_empty_for_none(self): + dd = DataDict2(_make_person()) + self.assertEqual(dd.primary_name.surname_list[0].origintype.string, "") + + def test_string_missing_field_falls_back_normally(self): + # A DataDict2 with no "string" key at all (e.g. a Name) must not be + # affected by the .string property -- it should fall through to + # ordinary attribute lookup rather than raising or returning "". + dd = DataDict2(_make_person()) + self.assertIsInstance(dd.primary_name.string, NoneData) + + if __name__ == "__main__": unittest.main() From e73e334e9f15e61ef50d3e0ab109d9a618333fa5 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 18:40:02 -0700 Subject: [PATCH 023/156] Fix names property nesting alternate_names and __radd__ reversing order `names` was `[self.primary_name] + [self.alternate_names]` -- the extra brackets around alternate_names nested the whole list as a single element instead of spreading its items in. Separately, DataList2.__radd__ returned `self + value` instead of the mathematically required `value + self` (Python calls b.__radd__(a) to compute `a + b`), so `plain_list + data_list2` -- the exact pattern used to loop over primary + alternate names -- came out reversed. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 7 +++++-- GrampyScript/tests/test_datadict2.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index e9d735334..2f11844b7 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -329,7 +329,7 @@ def surname(self): @property def names(self): - return DataList2([self.primary_name] + [self.alternate_names]) + return DataList2([self.primary_name] + self.alternate_names) @property def string(self): @@ -481,7 +481,10 @@ def __add__(self, value): return DataList2([x for x in self] + [x for x in value]) def __radd__(self, value): - return DataList2([x for x in self] + [x for x in value]) + # self is the right-hand operand here (Python calls b.__radd__(a) + # for `a + b`), so the result must be `value + self`, not `self + + # value` -- otherwise `plain_list + data_list2` comes out reversed. + return DataList2([x for x in value] + [x for x in self]) sa = None diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index 00fa2f066..d61ae373a 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -168,6 +168,22 @@ def test_family_gramps_id(self): self.assertEqual(dd.gramps_id, "F0007") self.assertEqual(dd["_class"], "Family") + def test_names_includes_alternate_names_in_order(self): + # Regression: `names` was `[self.primary_name] + [self.alternate_names]` + # -- the extra brackets nested the whole alternate_names list as one + # element instead of spreading it in, and __radd__ used to reverse + # the order on top of that. + p = _make_person(first="John") + alt = Name() + alt_sn = Surname() + alt_sn.set_surname("Doe") + alt.add_surname(alt_sn) + alt.set_first_name("Jack") + p.add_alternate_name(alt) + dd = DataDict2(p) + self.assertEqual(len(dd.names), 2) + self.assertEqual([n.first_name for n in dd.names], ["John", "Jack"]) + # --------------------------------------------------------------------------- # DataDict2 — surname/name/reference on non-Person and *Ref wrappers @@ -260,6 +276,14 @@ def test_add_concatenates(self): dl2 = DataList2([DataDict2(_make_person(gramps_id="I0002"))]) self.assertEqual(len(dl1 + dl2), 2) + def test_radd_preserves_order(self): + # Regression: __radd__ used to return `self + value` instead of + # `value + self`, so `plain_list + data_list2` (the pattern used to + # loop over primary + alternate names) came out reversed. + dl = DataList2([DataDict2(_make_person(gramps_id="I0002"))]) + combined = [DataDict2(_make_person(gramps_id="I0001"))] + dl + self.assertEqual([p.gramps_id for p in combined], ["I0001", "I0002"]) + def test_empty_list(self): dl = DataList2([]) self.assertEqual(len(dl), 0) From d51fe77e02d0586398bdc5c7d28d85ca0d41b566 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 18:46:13 -0700 Subject: [PATCH 024/156] Fix DataList2 fan-out of set_*() methods across all items `dl.set_privacy(True)` fanned out attribute access first, collecting each item's unevaluated set_*() wrapper closure into a DataList2 -- then failed to call, since a DataList2 of closures isn't callable itself. Special-case set_*() the same way DataDict2 already does: return one callable that applies the same args to every item in the list. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/datadict2.py | 9 ++++++++- GrampyScript/tests/test_datadict2.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/GrampyScript/datadict2.py b/GrampyScript/datadict2.py index 2f11844b7..2970365cf 100644 --- a/GrampyScript/datadict2.py +++ b/GrampyScript/datadict2.py @@ -443,7 +443,14 @@ def __setitem__(self, position, value): raise Exception("Setting a DataList2 item is not allowed") def __getattr__(self, attr): - # return DataList2(flatten([getattr(x, attr) for x in self])) + if attr.startswith("set_"): + # Fan the call (same args) out to every item, rather than + # collecting each item's set_*() wrapper closure unevaluated + # (which isn't callable itself and silently did nothing). + def wrapper(*args, **kwargs): + return DataList2([getattr(x, attr)(*args, **kwargs) for x in self]) + + return wrapper return DataList2(flatten([getattr(x, attr) for x in self])) def __getitem__(self, position, root=None, path=""): diff --git a/GrampyScript/tests/test_datadict2.py b/GrampyScript/tests/test_datadict2.py index d61ae373a..8460b106e 100644 --- a/GrampyScript/tests/test_datadict2.py +++ b/GrampyScript/tests/test_datadict2.py @@ -271,6 +271,17 @@ def test_getattr_fans_out_across_items(self): self.assertIn("I0001", ids) self.assertIn("I0002", ids) + def test_set_method_fans_out_across_items(self): + # Regression: `dl.set_privacy(True)` used to fan out attribute + # access first (collecting each item's unevaluated set_*() wrapper + # closure into a DataList2), then fail to call because a DataList2 + # of closures isn't itself callable. It must call set_privacy(True) + # on every item instead. + dl = self._make_list() + dl.set_privacy(True) + self.assertTrue(dl[0].private) + self.assertTrue(dl[1].private) + def test_add_concatenates(self): dl1 = DataList2([DataDict2(_make_person(gramps_id="I0001"))]) dl2 = DataList2([DataDict2(_make_person(gramps_id="I0002"))]) From 0966affd733a16cd2b1c7bb5846d9a443a25731c Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 19:04:10 -0700 Subject: [PATCH 025/156] Fix Grampy bulk changes not appearing in Undo/Redo history begin_changes()/end_changes() called the lowlevel db._txn_begin()/_txn_commit() (raw SQL BEGIN/COMMIT) instead of db.transaction_begin()/transaction_commit(), so the DbTxn was never pushed onto undodb and script edits were invisible to Undo/Redo despite being written to disk. Co-Authored-By: Claude Sonnet 5 --- GrampyScript/GrampyScript.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 896adb2ec..90a83ceda 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -1198,11 +1198,12 @@ def begin_changes(message=_("Gram.py Script Edited Data")): self.CHANGING = True self.TRANSACTION = DbTxn(message, self.db) - self.db._txn_begin() + self.db.transaction_begin(self.TRANSACTION) def end_changes(): if self.CHANGING: - self.db._txn_commit() + self.db.transaction_commit(self.TRANSACTION) + self.CHANGING = False def _iter_raw_person_data(): for handle, data in self.db._iter_raw_person_data(): From 68bd175a4d62af23ab72344d63b31abf9b73cf01 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Thu, 9 Jul 2026 08:43:00 -0700 Subject: [PATCH 026/156] Merge GrampyScript: bundled example scripts, Open-dialog previews, UI polish#978 --- GrampyScript/GrampyScript.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GrampyScript/GrampyScript.gpr.py b/GrampyScript/GrampyScript.gpr.py index 55ddf10a8..79c3a54c7 100644 --- a/GrampyScript/GrampyScript.gpr.py +++ b/GrampyScript/GrampyScript.gpr.py @@ -23,7 +23,7 @@ name=_("Gram.py Script"), description=_("Run a special Gramps Python script"), status=STABLE, - version = '0.0.7', + version = '0.0.8', fname="GrampyScript.py", authors=["Doug Blank"], authors_email=["doug.blank@gmail.com"], From a142b57722f571138a6b5ae7158e4dfde467e3c9 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 07:50:06 -0700 Subject: [PATCH 027/156] GrampsAssistant: document custom_filter() and delete() DSL functions PR #978 added custom_filter(name, namespace="Person") and delete(obj) to GrampyScript's execution scope. GrampsAssistant drives that same scope via tools.py's execute_script/evaluate_expression docstrings, so the model needs to know these exist to use or suggest them. Co-Authored-By: Claude Sonnet 5 --- GrampsAssistant/tools.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/GrampsAssistant/tools.py b/GrampsAssistant/tools.py index 3f48a9c16..025937bb5 100644 --- a/GrampsAssistant/tools.py +++ b/GrampsAssistant/tools.py @@ -1268,7 +1268,7 @@ def evaluate_expression(code: str) -> str: The same scope as execute_script is available: database, people(), families(), events(), places(), sources(), citations(), media(), notes(), repositories(), selected(), filtered(), - active_person, active_family, ..., today, counter() + custom_filter(), active_person, active_family, ..., today, counter() In addition, any Gramps lib class can be imported normally: from gramps.gen.lib import Person, Event, Date @@ -1313,6 +1313,9 @@ def execute_script(code: str) -> str: media(), notes(), repositories() -- all records of that type selected("Person") -- currently selected rows in the active view filtered("Person") -- currently filtered rows in the active view + custom_filter(name, namespace="Person") -- rows matching an existing + Gramps sidebar custom filter; prints a Warning and yields + nothing if no filter with that name exists for the namespace ("Person","Family","Event","Place","Source","Citation", "Media","Note","Repository" are valid table names) @@ -1332,6 +1335,8 @@ def execute_script(code: str) -> str: counter() -- defaultdict(int) for tallying begin_changes() -- open a DB transaction for edits end_changes() -- commit the transaction + delete(obj) -- delete a record (person, family, event, ...); + must be called between begin_changes() and end_changes() ## Person properties person.gramps_id -- "I0001" @@ -1389,6 +1394,12 @@ def execute_script(code: str) -> str: person.private = True # triggers auto-commit via callback end_changes() + begin_changes() + for repo in repositories(): + if not repo.back_references: + delete(repo) # deletes the record itself + end_changes() + Example -- people born before 1800: for person in people(): year = person.birth.get_date_object().get_year() From 00fb47494c082fc10b8560c331b770feee7aa400 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 7 Jul 2026 09:51:34 -0700 Subject: [PATCH 028/156] Added help URL --- GrampsAssistant/grampsassistant.gpr.py | 1 + GrampsAssistant/grampsassistant.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/GrampsAssistant/grampsassistant.gpr.py b/GrampsAssistant/grampsassistant.gpr.py index 017817ce6..d39e96526 100644 --- a/GrampsAssistant/grampsassistant.gpr.py +++ b/GrampsAssistant/grampsassistant.gpr.py @@ -36,4 +36,5 @@ optionclass="GrampsAssistantOptions", tool_modes=[TOOL_MODE_GUI], depends_on=["Grampy Script"], + help_url="Addon:GrampsAssistant", ) diff --git a/GrampsAssistant/grampsassistant.py b/GrampsAssistant/grampsassistant.py index 0d5589c05..c7b05c982 100644 --- a/GrampsAssistant/grampsassistant.py +++ b/GrampsAssistant/grampsassistant.py @@ -29,6 +29,7 @@ from gi.repository import GLib, Gdk, Gtk, Pango from gramps.gen.config import config as global_config +from gramps.gui.display import display_url try: from gramps.gui.sidepanel import BaseSidePanel @@ -44,6 +45,8 @@ _ = glocale.translation.gettext _LOG = logging.getLogger("gramps-assistant") +WIKI_PAGE = "https://gramps-project.org/wiki/index.php?title=Addon:GrampsAssistant" + # --------------------------------------------------------------------------- # Plugin-local configuration # --------------------------------------------------------------------------- @@ -213,6 +216,10 @@ def _build_ui(self): clear_btn.set_tooltip_text(_("Clear conversation and context")) clear_btn.connect("clicked", self._on_clear_clicked) + help_btn = Gtk.Button(label=_("Help")) + help_btn.set_tooltip_text(_("Open the Gramps Assistant wiki page")) + help_btn.connect("clicked", self._on_help_clicked) + self._send_btn = Gtk.Button(label=_("Send")) self._send_btn.connect("clicked", self._on_send_clicked) @@ -221,6 +228,7 @@ def _build_ui(self): btn_row.pack_start(settings_btn, False, False, 0) btn_row.pack_start(clear_btn, False, False, 0) + btn_row.pack_start(help_btn, False, False, 0) btn_row.pack_start(self._context_label, True, True, 4) btn_row.pack_end(self._send_btn, False, False, 0) @@ -707,6 +715,9 @@ def _on_clear_clicked(self, button): self._show_welcome() self._update_context_label() + def _on_help_clicked(self, button): + display_url(WIKI_PAGE) + # ------------------------------------------------------------------ # Message submission # ------------------------------------------------------------------ From dd6ee2fc94eccd9801e226c839009d688685699e Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Thu, 9 Jul 2026 08:46:22 -0700 Subject: [PATCH 029/156] Merge GrampsAssistant: document custom_filter() and delete() DSL functions; added help url #979 --- GrampsAssistant/grampsassistant.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GrampsAssistant/grampsassistant.gpr.py b/GrampsAssistant/grampsassistant.gpr.py index d39e96526..f7d70eacf 100644 --- a/GrampsAssistant/grampsassistant.gpr.py +++ b/GrampsAssistant/grampsassistant.gpr.py @@ -25,7 +25,7 @@ id="grampsassistant", name=_("Gramps Assistant"), description=_("AI assistant for querying your Gramps family tree"), - version = '1.0.1', + version = '1.0.2', gramps_target_version="6.1", status=STABLE, fname="grampsassistant.py", From c9171c90dc917c76d74ee4f8ab7210f0ac74522a Mon Sep 17 00:00:00 2001 From: Javad Razavian Date: Sat, 4 Jul 2026 01:55:52 +0200 Subject: [PATCH 030/156] Add DateOfDeathGramplet - gramplet listing death dates sorted by month and day --- .../DateOfDeathGramplet.gpr.py | 34 ++++++++ DateOfDeathGramplet/DateOfDeathGramplet.py | 79 +++++++++++++++++++ DateOfDeathGramplet/po/ca-local.po | 28 +++++++ DateOfDeathGramplet/po/da-local.po | 28 +++++++ DateOfDeathGramplet/po/de-local.po | 28 +++++++ DateOfDeathGramplet/po/es-local.po | 28 +++++++ DateOfDeathGramplet/po/fa-local.po | 27 +++++++ DateOfDeathGramplet/po/fi-local.po | 29 +++++++ DateOfDeathGramplet/po/fr-local.po | 28 +++++++ DateOfDeathGramplet/po/he-local.po | 29 +++++++ DateOfDeathGramplet/po/hr-local.po | 29 +++++++ DateOfDeathGramplet/po/hu-local.po | 28 +++++++ DateOfDeathGramplet/po/it-local.po | 28 +++++++ DateOfDeathGramplet/po/lt-local.po | 32 ++++++++ DateOfDeathGramplet/po/nb-local.po | 29 +++++++ DateOfDeathGramplet/po/nl-local.po | 28 +++++++ DateOfDeathGramplet/po/pl-local.po | 33 ++++++++ DateOfDeathGramplet/po/pt_BR-local.po | 27 +++++++ DateOfDeathGramplet/po/pt_PT-local.po | 28 +++++++ DateOfDeathGramplet/po/ru-local.po | 30 +++++++ DateOfDeathGramplet/po/sk-local.po | 28 +++++++ DateOfDeathGramplet/po/sv-local.po | 28 +++++++ DateOfDeathGramplet/po/template.pot | 35 ++++++++ DateOfDeathGramplet/po/tr-local.po | 31 ++++++++ DateOfDeathGramplet/po/uk-local.po | 29 +++++++ 25 files changed, 781 insertions(+) create mode 100644 DateOfDeathGramplet/DateOfDeathGramplet.gpr.py create mode 100644 DateOfDeathGramplet/DateOfDeathGramplet.py create mode 100644 DateOfDeathGramplet/po/ca-local.po create mode 100644 DateOfDeathGramplet/po/da-local.po create mode 100644 DateOfDeathGramplet/po/de-local.po create mode 100644 DateOfDeathGramplet/po/es-local.po create mode 100644 DateOfDeathGramplet/po/fa-local.po create mode 100644 DateOfDeathGramplet/po/fi-local.po create mode 100644 DateOfDeathGramplet/po/fr-local.po create mode 100644 DateOfDeathGramplet/po/he-local.po create mode 100644 DateOfDeathGramplet/po/hr-local.po create mode 100644 DateOfDeathGramplet/po/hu-local.po create mode 100644 DateOfDeathGramplet/po/it-local.po create mode 100644 DateOfDeathGramplet/po/lt-local.po create mode 100644 DateOfDeathGramplet/po/nb-local.po create mode 100644 DateOfDeathGramplet/po/nl-local.po create mode 100644 DateOfDeathGramplet/po/pl-local.po create mode 100644 DateOfDeathGramplet/po/pt_BR-local.po create mode 100644 DateOfDeathGramplet/po/pt_PT-local.po create mode 100644 DateOfDeathGramplet/po/ru-local.po create mode 100644 DateOfDeathGramplet/po/sk-local.po create mode 100644 DateOfDeathGramplet/po/sv-local.po create mode 100644 DateOfDeathGramplet/po/template.pot create mode 100644 DateOfDeathGramplet/po/tr-local.po create mode 100644 DateOfDeathGramplet/po/uk-local.po diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py new file mode 100644 index 000000000..f84ba0daa --- /dev/null +++ b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py @@ -0,0 +1,34 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Javad Razavian +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +register( + GRAMPLET, + id="DateOfDeath", + name=_("Date of Death"), + description=_("a gramplet that displays dates of death sorted by month and day"), + status=STABLE, + version = '1.0.2', + fname="DateOfDeathGramplet.py", + height=200, + gramplet="DateOfDeathGramplet", + gramps_target_version="6.0", + gramplet_title=_("Date of Death"), + help_url="DateOfDeathGramplet", +) diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.py b/DateOfDeathGramplet/DateOfDeathGramplet.py new file mode 100644 index 000000000..7d1b14e25 --- /dev/null +++ b/DateOfDeathGramplet/DateOfDeathGramplet.py @@ -0,0 +1,79 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Javad Razavian +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +from gramps.gen.plug import Gramplet +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.display.name import displayer as name_displayer +import gramps.gen.datehandler +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.gettext + + +class DateOfDeathGramplet(Gramplet): + def init(self): + self.set_text(_("No Family Tree loaded.")) + + def db_changed(self): + self.connect(self.dbstate.db, 'person-add', self.update) + self.connect(self.dbstate.db, 'person-delete', self.update) + self.connect(self.dbstate.db, 'person-update', self.update) + + def main(self): + self.set_text(_("Processing...")) + database = self.dbstate.db + self.result = [] + + for person in database.iter_people(): + death_ref = person.get_death_ref() + if not death_ref: + continue + death_event = database.get_event_from_handle(death_ref.ref) + date_of_death = death_event.get_date_object() + if not date_of_death.is_regular(): + continue + + age = "" + birth_ref = person.get_birth_ref() + if birth_ref: + birth = database.get_event_from_handle(birth_ref.ref) + birth_date = birth.get_date_object() + if birth_date.is_regular(): + age = date_of_death - birth_date + + self.result.append((date_of_death, person, age)) + + self.result.sort(key=lambda item: (item[0].get_month(), + item[0].get_day())) + self.clear_text() + + for date_of_death, person, age in self.result: + name = person.get_primary_name() + displayer = gramps.gen.datehandler.displayer + self.append_text("{}: ".format(displayer.display(date_of_death))) + self.link(name_displayer.display_name(name), "Person", + person.handle) + if age: + self.append_text(" ({})\n".format(age[0])) + else: + self.append_text("\n") + self.append_text("", scroll_to="begin") diff --git a/DateOfDeathGramplet/po/ca-local.po b/DateOfDeathGramplet/po/ca-local.po new file mode 100644 index 000000000..f28963406 --- /dev/null +++ b/DateOfDeathGramplet/po/ca-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: ca\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-09-03 03:01+0000\n" +"Last-Translator: Adolfo Jayme Barrientos \n" +"Language-Team: Catalan \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.13.1-dev\n" + +msgid "Date of Death" +msgstr "Data de defunció" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "un gramplet que mostra les dates de defunció ordenades per mes i dia" + +msgid "No Family Tree loaded." +msgstr "No hi ha cap arbre familiar carregat." + +msgid "Processing..." +msgstr "Processant…" + diff --git a/DateOfDeathGramplet/po/da-local.po b/DateOfDeathGramplet/po/da-local.po new file mode 100644 index 000000000..54a8d7d02 --- /dev/null +++ b/DateOfDeathGramplet/po/da-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-02-25 16:12+0000\n" +"Last-Translator: Kaj Arne Mikkelsen \n" +"Language-Team: Danish \n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.2-dev\n" + +msgid "Date of Death" +msgstr "Dødsdato" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "en gramplet der viser dødsdatoer sorteret efter måned og dag" + +msgid "No Family Tree loaded." +msgstr "Ingen stamtræ indlæst." + +msgid "Processing..." +msgstr "Behandler…" + diff --git a/DateOfDeathGramplet/po/de-local.po b/DateOfDeathGramplet/po/de-local.po new file mode 100644 index 000000000..022a5ec5c --- /dev/null +++ b/DateOfDeathGramplet/po/de-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: de\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-05-19 21:02+0000\n" +"Last-Translator: Mirko Leonhäuser \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.12-dev\n" + +msgid "Date of Death" +msgstr "Todesdatum" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "ein Gramplet, das die Todesdaten sortiert nach Monat und Tag anzeigt" + +msgid "No Family Tree loaded." +msgstr "Kein Stammbaum geladen." + +msgid "Processing..." +msgstr "Verarbeite…" + diff --git a/DateOfDeathGramplet/po/es-local.po b/DateOfDeathGramplet/po/es-local.po new file mode 100644 index 000000000..efb3f18fd --- /dev/null +++ b/DateOfDeathGramplet/po/es-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: GRAMPS 3.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2026-05-04 21:37+0000\n" +"Last-Translator: Francisco Serrador \n" +"Language-Team: Spanish \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.17.1\n" + +msgid "Date of Death" +msgstr "Fecha de fallecimiento" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "un gramplet que muestra las fechas de fallecimiento ordenadas por mes y día" + +msgid "No Family Tree loaded." +msgstr "No hay ningún árbol familiar cargado." + +msgid "Processing..." +msgstr "Procesando…" + diff --git a/DateOfDeathGramplet/po/fa-local.po b/DateOfDeathGramplet/po/fa-local.po new file mode 100644 index 000000000..0df3dd05c --- /dev/null +++ b/DateOfDeathGramplet/po/fa-local.po @@ -0,0 +1,27 @@ +msgid "" +msgstr "" +"Project-Id-Version: DateOfDeathGramplet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-04 12:00+0000\n" +"PO-Revision-Date: 2026-07-04 12:00+0000\n" +"Last-Translator: Javad Razavian \n" +"Language-Team: Persian \n" +"Language: fa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Weblate 5.13-dev\n" + +msgid "Date of Death" +msgstr "تاریخ فوت" + +msgid "a gramplet that displays death dates sorted by month and day" +msgstr "یک گرمپلت که تاریخ‌های فوت را مرتب بر اساس ماه و روز نمایش می‌دهد" + +msgid "No Family Tree loaded." +msgstr "هیچ شجره‌نامه‌ای بارگذاری نشده است." + +msgid "Processing..." +msgstr "در حال پردازش..." diff --git a/DateOfDeathGramplet/po/fi-local.po b/DateOfDeathGramplet/po/fi-local.po new file mode 100644 index 000000000..49558baeb --- /dev/null +++ b/DateOfDeathGramplet/po/fi-local.po @@ -0,0 +1,29 @@ +msgid "" +msgstr "" +"Project-Id-Version: fi\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-02-20 13:28+0000\n" +"Last-Translator: Matti Niemelä \n" +"Language-Team: Finnish \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.1-dev\n" +"Generated-By: pygettext.py 1.4\n" + +msgid "Date of Death" +msgstr "Kuolinpäivä" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "gramplet joka näyttää kuolinpäivät järjestettynä kuukauden ja päivän mukaan" + +msgid "No Family Tree loaded." +msgstr "Ei sukupuuta ladattu." + +msgid "Processing..." +msgstr "Käsitellään…" + diff --git a/DateOfDeathGramplet/po/fr-local.po b/DateOfDeathGramplet/po/fr-local.po new file mode 100644 index 000000000..3262efcf5 --- /dev/null +++ b/DateOfDeathGramplet/po/fr-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: trunk\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"PO-Revision-Date: 2026-05-14 19:17+0000\n" +"Last-Translator: \"David D.\" \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"X-Generator: Weblate 2026.5.dev0\n" + +msgid "Date of Death" +msgstr "Date de décès" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "un gramplet qui affiche les dates de décès triées par mois et jour" + +msgid "No Family Tree loaded." +msgstr "Aucun arbre généalogique chargé." + +msgid "Processing..." +msgstr "Traitement en cours…" + diff --git a/DateOfDeathGramplet/po/he-local.po b/DateOfDeathGramplet/po/he-local.po new file mode 100644 index 000000000..a2a33767f --- /dev/null +++ b/DateOfDeathGramplet/po/he-local.po @@ -0,0 +1,29 @@ +msgid "" +msgstr "" +"Project-Id-Version: Gramps 5.2.0 – mediamerge\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-08-11 18:01+0000\n" +"Last-Translator: Avi Markovitz \n" +"Language-Team: Hebrew \n" +"Language: he\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " +"n % 10 == 0) ? 2 : 3));\n" +"X-Generator: Weblate 5.13-dev\n" + +msgid "Date of Death" +msgstr "תאריך פטירה" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "גרמפלט המציג תאריכי פטירה ממוינים לפי חודש ויום" + +msgid "No Family Tree loaded." +msgstr "לא נטען עץ משפחה." + +msgid "Processing..." +msgstr "מעבד…" + diff --git a/DateOfDeathGramplet/po/hr-local.po b/DateOfDeathGramplet/po/hr-local.po new file mode 100644 index 000000000..d4f0c52a9 --- /dev/null +++ b/DateOfDeathGramplet/po/hr-local.po @@ -0,0 +1,29 @@ +msgid "" +msgstr "" +"Project-Id-Version: Gramps 5.x\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-03-02 14:58+0000\n" +"Last-Translator: Milo Ivir \n" +"Language-Team: Croatian \n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Weblate 5.10.3-dev\n" + +msgid "Date of Death" +msgstr "Datum smrti" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "gramplet koji prikazuje datume smrti poredane po mjesecu i danu" + +msgid "No Family Tree loaded." +msgstr "Nije učitano obiteljsko stablo." + +msgid "Processing..." +msgstr "Obrađujem…" + diff --git a/DateOfDeathGramplet/po/hu-local.po b/DateOfDeathGramplet/po/hu-local.po new file mode 100644 index 000000000..7857f2e50 --- /dev/null +++ b/DateOfDeathGramplet/po/hu-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: hu\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2026-02-21 14:09+0000\n" +"Last-Translator: Daniel Szollosi-Nagy \n" +"Language-Team: Hungarian \n" +"Language: hu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.16.1-dev\n" + +msgid "Date of Death" +msgstr "Halálozási dátum" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "egy gramplet amely a halálozási dátumokat jeleníti meg hónap és nap szerint rendezve" + +msgid "No Family Tree loaded." +msgstr "Nincs családfa betöltve." + +msgid "Processing..." +msgstr "Feldolgozás…" + diff --git a/DateOfDeathGramplet/po/it-local.po b/DateOfDeathGramplet/po/it-local.po new file mode 100644 index 000000000..5fada08c1 --- /dev/null +++ b/DateOfDeathGramplet/po/it-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: gramps 3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-09-06 11:01+0000\n" +"Last-Translator: Luigi Toscano \n" +"Language-Team: Italian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14-dev\n" + +msgid "Date of Death" +msgstr "Data di morte" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "un gramplet che mostra le date di morte ordinate per mese e giorno" + +msgid "No Family Tree loaded." +msgstr "Nessun albero genealogico caricato." + +msgid "Processing..." +msgstr "Elaborazione in corso…" + diff --git a/DateOfDeathGramplet/po/lt-local.po b/DateOfDeathGramplet/po/lt-local.po new file mode 100644 index 000000000..5ada4a121 --- /dev/null +++ b/DateOfDeathGramplet/po/lt-local.po @@ -0,0 +1,32 @@ +msgid "" +msgstr "" +"Project-Id-Version: lt\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-08-27 08:02+0000\n" +"Last-Translator: Tadas Masiulionis \n" +"Language-Team: Lithuanian \n" +"Language: lt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"(n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Weblate 5.13\n" +"Generated-By: pygettext.py 1.4\n" +"X-Poedit-Language: Lithuanian\n" +"X-Poedit-Country: LITHUANIA\n" + +msgid "Date of Death" +msgstr "Mirties data" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "grampletas, rodantis mirties datas, surūšiuotas pagal mėnesį ir dieną" + +msgid "No Family Tree loaded." +msgstr "Neįkeltas joks šeimos medis." + +msgid "Processing..." +msgstr "Apdorojama…" + diff --git a/DateOfDeathGramplet/po/nb-local.po b/DateOfDeathGramplet/po/nb-local.po new file mode 100644 index 000000000..8031a3a95 --- /dev/null +++ b/DateOfDeathGramplet/po/nb-local.po @@ -0,0 +1,29 @@ +msgid "" +msgstr "" +"Project-Id-Version: nb\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"PO-Revision-Date: 2026-06-01 12:35+0000\n" +"Last-Translator: Harald Herreros \n" +"Language-Team: Norwegian Bokmål \n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 2026.6\n" +"Generated-By: pygettext.py 1.4\n" + +msgid "Date of Death" +msgstr "Dødsdato" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "en gramplet som viser dødsdatoer sortert etter måned og dag" + +msgid "No Family Tree loaded." +msgstr "Ingen familietre lastet." + +msgid "Processing..." +msgstr "Behandler…" + diff --git a/DateOfDeathGramplet/po/nl-local.po b/DateOfDeathGramplet/po/nl-local.po new file mode 100644 index 000000000..4c659415f --- /dev/null +++ b/DateOfDeathGramplet/po/nl-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: MediaMerge 5.x\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-04-14 22:32+0000\n" +"Last-Translator: Stephan Paternotte \n" +"Language-Team: Dutch \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.11-dev\n" + +msgid "Date of Death" +msgstr "Overlijdensdatum" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "een gramplet dat de overlijdensdata toont gesorteerd op maand en dag" + +msgid "No Family Tree loaded." +msgstr "Geen stamboom geladen." + +msgid "Processing..." +msgstr "Bezig met verwerken…" + diff --git a/DateOfDeathGramplet/po/pl-local.po b/DateOfDeathGramplet/po/pl-local.po new file mode 100644 index 000000000..3d36f21ea --- /dev/null +++ b/DateOfDeathGramplet/po/pl-local.po @@ -0,0 +1,33 @@ +msgid "" +msgstr "" +"Project-Id-Version: GRAMPS 3.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-08-23 15:02+0000\n" +"Last-Translator: Krystian Safjan \n" +"Language-Team: Polish \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Weblate 5.13\n" +"X-Poedit-Language: Polish\n" +"X-Poedit-Country: POLAND\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: ~/.poedit/a\n" + +msgid "Date of Death" +msgstr "Data śmierci" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "gramplet wyświetlający daty śmierci posortowane według miesiąca i dnia" + +msgid "No Family Tree loaded." +msgstr "Nie załadowano drzewa genealogicznego." + +msgid "Processing..." +msgstr "Przetwarzanie…" + diff --git a/DateOfDeathGramplet/po/pt_BR-local.po b/DateOfDeathGramplet/po/pt_BR-local.po new file mode 100644 index 000000000..4bc5c638d --- /dev/null +++ b/DateOfDeathGramplet/po/pt_BR-local.po @@ -0,0 +1,27 @@ +msgid "" +msgstr "" +"Project-Id-Version: trunk\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2012-08-26 20:57-0300\n" +"Last-Translator: André Marcelo Alvarenga \n" +"Language-Team: Brazilian Portuguese>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 1.0\n" + +msgid "Date of Death" +msgstr "Data de falecimento" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "um gramplet que mostra as datas de falecimento ordenadas por mês e dia" + +msgid "No Family Tree loaded." +msgstr "Nenhuma árvore familiar carregada." + +msgid "Processing..." +msgstr "Processando…" + diff --git a/DateOfDeathGramplet/po/pt_PT-local.po b/DateOfDeathGramplet/po/pt_PT-local.po new file mode 100644 index 000000000..1400ca71e --- /dev/null +++ b/DateOfDeathGramplet/po/pt_PT-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: gramps51\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-03-08 07:05+0000\n" +"Last-Translator: Pedro Albuquerque \n" +"Language-Team: Portuguese (Portugal) \n" +"Language: pt_PT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.3-dev\n" + +msgid "Date of Death" +msgstr "Data de falecimento" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "um gramplet que mostra as datas de falecimento ordenadas por mês e dia" + +msgid "No Family Tree loaded." +msgstr "Nenhuma árvore genealógica carregada." + +msgid "Processing..." +msgstr "A processar…" + diff --git a/DateOfDeathGramplet/po/ru-local.po b/DateOfDeathGramplet/po/ru-local.po new file mode 100644 index 000000000..40df974eb --- /dev/null +++ b/DateOfDeathGramplet/po/ru-local.po @@ -0,0 +1,30 @@ +msgid "" +msgstr "" +"Project-Id-Version: gramps50\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2018-12-04 16:36+0300\n" +"Last-Translator: Ivan Komaritsyn \n" +"Language-Team: Russian\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Gtranslator 2.91.7\n" +"X-Poedit-Language: Russian\n" +"X-Poedit-Country: RUSSIAN FEDERATION\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +msgid "Date of Death" +msgstr "Дата смерти" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "грамплет, отображающий даты смерти, отсортированные по месяцу и дню" + +msgid "No Family Tree loaded." +msgstr "Не загружено ни одного семейного древа." + +msgid "Processing..." +msgstr "Обработка…" + diff --git a/DateOfDeathGramplet/po/sk-local.po b/DateOfDeathGramplet/po/sk-local.po new file mode 100644 index 000000000..c7b02743c --- /dev/null +++ b/DateOfDeathGramplet/po/sk-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: GRAMPS 3.1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2026-05-11 10:34+0000\n" +"Last-Translator: Milan \n" +"Language-Team: Slovak \n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" +"X-Generator: Weblate 2026.5-dev\n" + +msgid "Date of Death" +msgstr "Dátum úmrtia" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "gramplet zobrazujúci dátumy úmrtia zoradené podľa mesiaca a dňa" + +msgid "No Family Tree loaded." +msgstr "Nie je načítaný žiadny rodokmeň." + +msgid "Processing..." +msgstr "Spracúvam…" + diff --git a/DateOfDeathGramplet/po/sv-local.po b/DateOfDeathGramplet/po/sv-local.po new file mode 100644 index 000000000..84e4d4834 --- /dev/null +++ b/DateOfDeathGramplet/po/sv-local.po @@ -0,0 +1,28 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-05-26 07:15+0000\n" +"Last-Translator: Pär Ekholm \n" +"Language-Team: Swedish \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.12-dev\n" + +msgid "Date of Death" +msgstr "Dödsdatum" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "en gramplet som visar dödsdatum sorterade efter månad och dag" + +msgid "No Family Tree loaded." +msgstr "Inget släktträd laddat." + +msgid "Processing..." +msgstr "Bearbetar…" + diff --git a/DateOfDeathGramplet/po/template.pot b/DateOfDeathGramplet/po/template.pot new file mode 100644 index 000000000..76ab76e83 --- /dev/null +++ b/DateOfDeathGramplet/po/template.pot @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-04 12:00+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:25 +#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:34 +msgid "Date of Death" +msgstr "" + +#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:26 +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "" + +#: DateOfDeathGramplet/DateOfDeathGramplet.py:28 +msgid "No Family Tree loaded." +msgstr "" + +#: DateOfDeathGramplet/DateOfDeathGramplet.py:39 +msgid "Processing..." +msgstr "" diff --git a/DateOfDeathGramplet/po/tr-local.po b/DateOfDeathGramplet/po/tr-local.po new file mode 100644 index 000000000..5ae402e9a --- /dev/null +++ b/DateOfDeathGramplet/po/tr-local.po @@ -0,0 +1,31 @@ +msgid "" +msgstr "" +"Project-Id-Version: 4.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"PO-Revision-Date: 2026-05-30 20:01+0000\n" +"Last-Translator: Osman Öz \n" +"Language-Team: Turkish \n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 2026.6.dev0\n" +"Generated-By: pygettext.py 1.4\n" +"X-Language: tr\n" +"X-Source-Language: C\n" + +msgid "Date of Death" +msgstr "Ölüm tarihi" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "ölüm tarihlerini aya ve güne göre sıralayan bir gramplet" + +msgid "No Family Tree loaded." +msgstr "Hiçbir aile ağacı yüklenmedi." + +msgid "Processing..." +msgstr "İşleniyor…" + diff --git a/DateOfDeathGramplet/po/uk-local.po b/DateOfDeathGramplet/po/uk-local.po new file mode 100644 index 000000000..f9fdd2aaa --- /dev/null +++ b/DateOfDeathGramplet/po/uk-local.po @@ -0,0 +1,29 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"PO-Revision-Date: 2025-03-06 13:57+0000\n" +"Last-Translator: Yurii Liubymyi \n" +"Language-Team: Ukrainian \n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Weblate 5.10.3-dev\n" + +msgid "Date of Death" +msgstr "Дата смерті" + +msgid "a gramplet that displays dates of death sorted by month and day" +msgstr "грамплет, який відображає дати смерті, відсортовані за місяцем та днем" + +msgid "No Family Tree loaded." +msgstr "Не завантажено жодного родинного дерева." + +msgid "Processing..." +msgstr "Обробка…" + From 21d1ce47842623592033b72502fb8e743d4c073e Mon Sep 17 00:00:00 2001 From: Javad Razavian Date: Sat, 4 Jul 2026 14:08:48 +0200 Subject: [PATCH 031/156] DateOfDeathGramplet: add proximity sort option, fix cross-calendar sort, update description - Add sort mode dropdown (proximity/month-day) matching BirthdaysGramplet - Fix cross-calendar sort by using gregorian() before constructing death_this_year - Move death/birth ref checks into __calculate() for cleaner main() - Update description string in .gpr.py and all .po files - Bump version to 1.1.0 --- .../DateOfDeathGramplet.gpr.py | 6 +- DateOfDeathGramplet/DateOfDeathGramplet.py | 79 +++++++++++++++---- DateOfDeathGramplet/po/ca-local.po | 16 +++- DateOfDeathGramplet/po/da-local.po | 16 +++- DateOfDeathGramplet/po/de-local.po | 16 +++- DateOfDeathGramplet/po/es-local.po | 17 +++- DateOfDeathGramplet/po/fa-local.po | 18 ++++- DateOfDeathGramplet/po/fi-local.po | 17 +++- DateOfDeathGramplet/po/fr-local.po | 16 +++- DateOfDeathGramplet/po/he-local.po | 16 +++- DateOfDeathGramplet/po/hr-local.po | 16 +++- DateOfDeathGramplet/po/hu-local.po | 18 ++++- DateOfDeathGramplet/po/it-local.po | 16 +++- DateOfDeathGramplet/po/lt-local.po | 16 +++- DateOfDeathGramplet/po/nb-local.po | 16 +++- DateOfDeathGramplet/po/nl-local.po | 16 +++- DateOfDeathGramplet/po/pl-local.po | 16 +++- DateOfDeathGramplet/po/pt_BR-local.po | 16 +++- DateOfDeathGramplet/po/pt_PT-local.po | 16 +++- DateOfDeathGramplet/po/ru-local.po | 16 +++- DateOfDeathGramplet/po/sk-local.po | 16 +++- DateOfDeathGramplet/po/sv-local.po | 16 +++- DateOfDeathGramplet/po/template.pot | 18 +++-- DateOfDeathGramplet/po/tr-local.po | 16 +++- DateOfDeathGramplet/po/uk-local.po | 16 +++- 25 files changed, 350 insertions(+), 111 deletions(-) diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py index f84ba0daa..596e778c5 100644 --- a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py +++ b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py @@ -22,13 +22,13 @@ GRAMPLET, id="DateOfDeath", name=_("Date of Death"), - description=_("a gramplet that displays dates of death sorted by month and day"), + description=_("a gramplet that displays death dates in sorted order"), status=STABLE, - version = '1.0.2', + version = '1.1.0', fname="DateOfDeathGramplet.py", height=200, gramplet="DateOfDeathGramplet", - gramps_target_version="6.0", + gramps_target_version="6.1", gramplet_title=_("Date of Death"), help_url="DateOfDeathGramplet", ) diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.py b/DateOfDeathGramplet/DateOfDeathGramplet.py index 7d1b14e25..045bfc0a5 100644 --- a/DateOfDeathGramplet/DateOfDeathGramplet.py +++ b/DateOfDeathGramplet/DateOfDeathGramplet.py @@ -21,7 +21,9 @@ from gramps.gen.plug import Gramplet from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.display.name import displayer as name_displayer +from gramps.gen.lib.date import Today, Date, gregorian import gramps.gen.datehandler +from gramps.gen.plug.menu import EnumeratedListOption try: _trans = glocale.get_addon_translator(__file__) except ValueError: @@ -32,6 +34,29 @@ class DateOfDeathGramplet(Gramplet): def init(self): self.set_text(_("No Family Tree loaded.")) + self.sort_mode = 'proximity' + + def build_options(self): + name_sort = _("Sort dates of death by") + self.opt_sort = EnumeratedListOption(name_sort, self.sort_mode) + self.opt_sort.add_item("proximity", _("Proximity to current date")) + self.opt_sort.add_item("month_day", _("Month and day")) + + self.add_option(self.opt_sort) + + def save_options(self): + self.sort_mode = self.opt_sort.get_value() + + def save_update_options(self, obj): + self.save_options() + self.gui.data = [self.sort_mode] + self.update() + + def on_load(self): + if len(self.gui.data) >= 1: + self.sort_mode = self.gui.data[0] + else: + self.sort_mode = 'proximity' def db_changed(self): self.connect(self.dbstate.db, 'person-add', self.update) @@ -52,28 +77,54 @@ def main(self): if not date_of_death.is_regular(): continue - age = "" - birth_ref = person.get_birth_ref() - if birth_ref: - birth = database.get_event_from_handle(birth_ref.ref) - birth_date = birth.get_date_object() - if birth_date.is_regular(): - age = date_of_death - birth_date + self.__calculate(database, person) - self.result.append((date_of_death, person, age)) - - self.result.sort(key=lambda item: (item[0].get_month(), - item[0].get_day())) + sort_by = self.opt_sort.get_value() + if sort_by == "proximity": + self.result.sort(key=lambda item: -item[0]) + else: + self.result.sort(key=lambda item: (item[1].get_month(), + item[1].get_day())) self.clear_text() - for date_of_death, person, age in self.result: + for diff_days, date, person, age in self.result: name = person.get_primary_name() displayer = gramps.gen.datehandler.displayer - self.append_text("{}: ".format(displayer.display(date_of_death))) + self.append_text("{}: ".format(displayer.display(date))) self.link(name_displayer.display_name(name), "Person", person.handle) if age: - self.append_text(" ({})\n".format(age[0])) + self.append_text(" ({})\n".format(age)) else: self.append_text("\n") self.append_text("", scroll_to="begin") + + def __calculate(self, database, person): + today = Today() + death_ref = person.get_death_ref() + if not death_ref: + return + death_event = database.get_event_from_handle(death_ref.ref) + date_of_death = death_event.get_date_object() + if not date_of_death.is_regular(): + return + + death_greg = gregorian(date_of_death) + death_this_year = Date(today.get_year(), + death_greg.get_month(), + death_greg.get_day()) + diff = today - death_this_year + diff_days = diff[1] * 30 + diff[2] + + birth_ref = person.get_birth_ref() + age = "" + if birth_ref: + birth = database.get_event_from_handle(birth_ref.ref) + birth_date = birth.get_date_object() + if birth_date.is_regular(): + age = date_of_death - birth_date + + if diff_days <= 0: + self.result.append((diff_days, date_of_death, person, age)) + else: + self.result.append((diff_days - 365, date_of_death, person, age)) diff --git a/DateOfDeathGramplet/po/ca-local.po b/DateOfDeathGramplet/po/ca-local.po index f28963406..5007e55cf 100644 --- a/DateOfDeathGramplet/po/ca-local.po +++ b/DateOfDeathGramplet/po/ca-local.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: ca\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"POT-Creation-Date: 2026-07-04 00:00-0000\n" "PO-Revision-Date: 2025-09-03 03:01+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Catalan \n" "Language-Team: Danish \n" "Language-Team: German \n" "Language-Team: Spanish \n" "Language-Team: Persian \n" "Language-Team: Finnish \n" "Language-Team: French \n" "Language-Team: Hebrew \n" "Language-Team: Croatian \n" "Language-Team: Hungarian \n" "Language-Team: Italian \n" "Language-Team: Lithuanian \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch \n" "Language-Team: Polish \n" "Language-Team: Brazilian Portuguese>\n" @@ -16,12 +16,20 @@ msgstr "" msgid "Date of Death" msgstr "Data de falecimento" -msgid "a gramplet that displays dates of death sorted by month and day" -msgstr "um gramplet que mostra as datas de falecimento ordenadas por mês e dia" +msgid "a gramplet that displays death dates in sorted order" +msgstr "" msgid "No Family Tree loaded." msgstr "Nenhuma árvore familiar carregada." +msgid "Sort dates of death by" +msgstr "Classifique as datas da morte por" + +msgid "Month and day" +msgstr "Mês e dia" + +msgid "Proximity to current date" +msgstr "Proximidade da data atual" + msgid "Processing..." msgstr "Processando…" - diff --git a/DateOfDeathGramplet/po/pt_PT-local.po b/DateOfDeathGramplet/po/pt_PT-local.po index 1400ca71e..b2b41d4c2 100644 --- a/DateOfDeathGramplet/po/pt_PT-local.po +++ b/DateOfDeathGramplet/po/pt_PT-local.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"POT-Creation-Date: 2026-07-04 00:00-0000\n" "PO-Revision-Date: 2025-03-08 07:05+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) \n" "Language-Team: Russian\n" @@ -19,12 +19,20 @@ msgstr "" msgid "Date of Death" msgstr "Дата смерти" -msgid "a gramplet that displays dates of death sorted by month and day" -msgstr "грамплет, отображающий даты смерти, отсортированные по месяцу и дню" +msgid "a gramplet that displays death dates in sorted order" +msgstr "" msgid "No Family Tree loaded." msgstr "Не загружено ни одного семейного древа." +msgid "Sort dates of death by" +msgstr "Сортировать даты смерти по" + +msgid "Month and day" +msgstr "Месяц и день" + +msgid "Proximity to current date" +msgstr "Близость к текущей дате" + msgid "Processing..." msgstr "Обработка…" - diff --git a/DateOfDeathGramplet/po/sk-local.po b/DateOfDeathGramplet/po/sk-local.po index c7b02743c..3096c4ec0 100644 --- a/DateOfDeathGramplet/po/sk-local.po +++ b/DateOfDeathGramplet/po/sk-local.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-06 08:56-0800\n" +"POT-Creation-Date: 2026-07-04 00:00-0000\n" "PO-Revision-Date: 2026-05-11 10:34+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak \n" "Language-Team: Swedish \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,23 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:25 -#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:34 msgid "Date of Death" msgstr "" -#: DateOfDeathGramplet/DateOfDeathGramplet.gpr.py:26 -msgid "a gramplet that displays dates of death sorted by month and day" +msgid "a gramplet that displays death dates in sorted order" msgstr "" -#: DateOfDeathGramplet/DateOfDeathGramplet.py:28 msgid "No Family Tree loaded." msgstr "" -#: DateOfDeathGramplet/DateOfDeathGramplet.py:39 +msgid "Sort dates of death by" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + msgid "Processing..." msgstr "" diff --git a/DateOfDeathGramplet/po/tr-local.po b/DateOfDeathGramplet/po/tr-local.po index 5ae402e9a..b9d400db9 100644 --- a/DateOfDeathGramplet/po/tr-local.po +++ b/DateOfDeathGramplet/po/tr-local.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"POT-Creation-Date: 2026-07-04 00:00-0000\n" "PO-Revision-Date: 2026-05-30 20:01+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish \n" "Language-Team: Ukrainian Date: Sat, 4 Jul 2026 15:10:59 +0200 Subject: [PATCH 032/156] DateOfDeathGramplet: fill translations for updated description string Update all 22 .po files with adapted translations for the new description 'a gramplet that displays death dates in sorted order' (removed 'month and day' reference). --- DateOfDeathGramplet/po/ca-local.po | 2 +- DateOfDeathGramplet/po/da-local.po | 2 +- DateOfDeathGramplet/po/de-local.po | 2 +- DateOfDeathGramplet/po/es-local.po | 2 +- DateOfDeathGramplet/po/fa-local.po | 2 +- DateOfDeathGramplet/po/fi-local.po | 2 +- DateOfDeathGramplet/po/fr-local.po | 2 +- DateOfDeathGramplet/po/he-local.po | 2 +- DateOfDeathGramplet/po/hr-local.po | 2 +- DateOfDeathGramplet/po/hu-local.po | 2 +- DateOfDeathGramplet/po/it-local.po | 2 +- DateOfDeathGramplet/po/lt-local.po | 2 +- DateOfDeathGramplet/po/nb-local.po | 2 +- DateOfDeathGramplet/po/nl-local.po | 2 +- DateOfDeathGramplet/po/pl-local.po | 2 +- DateOfDeathGramplet/po/pt_BR-local.po | 2 +- DateOfDeathGramplet/po/pt_PT-local.po | 2 +- DateOfDeathGramplet/po/ru-local.po | 2 +- DateOfDeathGramplet/po/sk-local.po | 2 +- DateOfDeathGramplet/po/sv-local.po | 2 +- DateOfDeathGramplet/po/tr-local.po | 2 +- DateOfDeathGramplet/po/uk-local.po | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/DateOfDeathGramplet/po/ca-local.po b/DateOfDeathGramplet/po/ca-local.po index 5007e55cf..9c4f1da3d 100644 --- a/DateOfDeathGramplet/po/ca-local.po +++ b/DateOfDeathGramplet/po/ca-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Data de defunció" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "un gramplet que mostra les dates de defunció ordenades" msgid "No Family Tree loaded." msgstr "No hi ha cap arbre familiar carregat." diff --git a/DateOfDeathGramplet/po/da-local.po b/DateOfDeathGramplet/po/da-local.po index 77d61040e..43b61a8af 100644 --- a/DateOfDeathGramplet/po/da-local.po +++ b/DateOfDeathGramplet/po/da-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Dødsdato" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "en gramplet der viser dødsdatoer sorteret" msgid "No Family Tree loaded." msgstr "Ingen stamtræ indlæst." diff --git a/DateOfDeathGramplet/po/de-local.po b/DateOfDeathGramplet/po/de-local.po index c06b73bea..31d8777e9 100644 --- a/DateOfDeathGramplet/po/de-local.po +++ b/DateOfDeathGramplet/po/de-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Todesdatum" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "ein Gramplet, das die Todesdaten sortiert anzeigt" msgid "No Family Tree loaded." msgstr "Kein Stammbaum geladen." diff --git a/DateOfDeathGramplet/po/es-local.po b/DateOfDeathGramplet/po/es-local.po index f88cf6685..d900b6eb8 100644 --- a/DateOfDeathGramplet/po/es-local.po +++ b/DateOfDeathGramplet/po/es-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Fecha de fallecimiento" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "un gramplet que muestra las fechas de fallecimiento ordenadas" "un gramplet que muestra las fechas de fallecimiento ordenadas por mes y día" msgid "No Family Tree loaded." diff --git a/DateOfDeathGramplet/po/fa-local.po b/DateOfDeathGramplet/po/fa-local.po index 02eae53b9..a9d5d31f6 100644 --- a/DateOfDeathGramplet/po/fa-local.po +++ b/DateOfDeathGramplet/po/fa-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "تاریخ فوت" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "نموداری که تاریخ‌های مرگ را به ترتیب مرتب‌شده نمایش می‌دهد" msgid "No Family Tree loaded." msgstr "هیچ شجره‌نامه‌ای بارگذاری نشده است." diff --git a/DateOfDeathGramplet/po/fi-local.po b/DateOfDeathGramplet/po/fi-local.po index adc0a8842..c5895ae0e 100644 --- a/DateOfDeathGramplet/po/fi-local.po +++ b/DateOfDeathGramplet/po/fi-local.po @@ -19,7 +19,7 @@ msgid "Date of Death" msgstr "Kuolinpäivä" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "gramplet joka näyttää kuolinpäivät järjestettynä" "gramplet joka näyttää kuolinpäivät järjestettynä kuukauden ja päivän mukaan" msgid "No Family Tree loaded." diff --git a/DateOfDeathGramplet/po/fr-local.po b/DateOfDeathGramplet/po/fr-local.po index bc900e96a..06b1c6e93 100644 --- a/DateOfDeathGramplet/po/fr-local.po +++ b/DateOfDeathGramplet/po/fr-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Date de décès" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "un gramplet qui affiche les dates de décès triées" msgid "No Family Tree loaded." msgstr "Aucun arbre généalogique chargé." diff --git a/DateOfDeathGramplet/po/he-local.po b/DateOfDeathGramplet/po/he-local.po index 86bd79878..6fe19b913 100644 --- a/DateOfDeathGramplet/po/he-local.po +++ b/DateOfDeathGramplet/po/he-local.po @@ -19,7 +19,7 @@ msgid "Date of Death" msgstr "תאריך פטירה" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "גרמפלט המציג תאריכי פטירה ממוינים" msgid "No Family Tree loaded." msgstr "לא נטען עץ משפחה." diff --git a/DateOfDeathGramplet/po/hr-local.po b/DateOfDeathGramplet/po/hr-local.po index 4c0381010..9d80f69a2 100644 --- a/DateOfDeathGramplet/po/hr-local.po +++ b/DateOfDeathGramplet/po/hr-local.po @@ -19,7 +19,7 @@ msgid "Date of Death" msgstr "Datum smrti" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "gramplet koji prikazuje datume smrti poredane" msgid "No Family Tree loaded." msgstr "Nije učitano obiteljsko stablo." diff --git a/DateOfDeathGramplet/po/hu-local.po b/DateOfDeathGramplet/po/hu-local.po index 9df5b0076..4e2a1578b 100644 --- a/DateOfDeathGramplet/po/hu-local.po +++ b/DateOfDeathGramplet/po/hu-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Halálozási dátum" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "egy gramplet amely a halálozási dátumokat rendezve jeleníti meg" "egy gramplet amely a halálozási dátumokat jeleníti meg hónap és nap szerint " "rendezve" diff --git a/DateOfDeathGramplet/po/it-local.po b/DateOfDeathGramplet/po/it-local.po index 11f6544e2..5268afef2 100644 --- a/DateOfDeathGramplet/po/it-local.po +++ b/DateOfDeathGramplet/po/it-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Data di morte" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "un gramplet che mostra le date di morte ordinate" msgid "No Family Tree loaded." msgstr "Nessun albero genealogico caricato." diff --git a/DateOfDeathGramplet/po/lt-local.po b/DateOfDeathGramplet/po/lt-local.po index b9695b3cc..2404535be 100644 --- a/DateOfDeathGramplet/po/lt-local.po +++ b/DateOfDeathGramplet/po/lt-local.po @@ -22,7 +22,7 @@ msgid "Date of Death" msgstr "Mirties data" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "grampletas, rodantis mirties datas, surūšiuotas" msgid "No Family Tree loaded." msgstr "Neįkeltas joks šeimos medis." diff --git a/DateOfDeathGramplet/po/nb-local.po b/DateOfDeathGramplet/po/nb-local.po index 2858a5738..ce32c6ae3 100644 --- a/DateOfDeathGramplet/po/nb-local.po +++ b/DateOfDeathGramplet/po/nb-local.po @@ -19,7 +19,7 @@ msgid "Date of Death" msgstr "Dødsdato" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "en gramplet som viser dødsdatoer sortert" msgid "No Family Tree loaded." msgstr "Ingen familietre lastet." diff --git a/DateOfDeathGramplet/po/nl-local.po b/DateOfDeathGramplet/po/nl-local.po index 6ff7e2620..202fdbc4d 100644 --- a/DateOfDeathGramplet/po/nl-local.po +++ b/DateOfDeathGramplet/po/nl-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Overlijdensdatum" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "een gramplet dat de overlijdensdata gesorteerd toont" msgid "No Family Tree loaded." msgstr "Geen stamboom geladen." diff --git a/DateOfDeathGramplet/po/pl-local.po b/DateOfDeathGramplet/po/pl-local.po index a24b3600d..7112a0598 100644 --- a/DateOfDeathGramplet/po/pl-local.po +++ b/DateOfDeathGramplet/po/pl-local.po @@ -23,7 +23,7 @@ msgid "Date of Death" msgstr "Data śmierci" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "gramplet wyświetlający daty śmierci w posortowanej kolejności" msgid "No Family Tree loaded." msgstr "Nie załadowano drzewa genealogicznego." diff --git a/DateOfDeathGramplet/po/pt_BR-local.po b/DateOfDeathGramplet/po/pt_BR-local.po index 6f8d383f0..917a57b12 100644 --- a/DateOfDeathGramplet/po/pt_BR-local.po +++ b/DateOfDeathGramplet/po/pt_BR-local.po @@ -17,7 +17,7 @@ msgid "Date of Death" msgstr "Data de falecimento" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "um gramplet que mostra as datas de falecimento ordenadas" msgid "No Family Tree loaded." msgstr "Nenhuma árvore familiar carregada." diff --git a/DateOfDeathGramplet/po/pt_PT-local.po b/DateOfDeathGramplet/po/pt_PT-local.po index b2b41d4c2..eb31eef07 100644 --- a/DateOfDeathGramplet/po/pt_PT-local.po +++ b/DateOfDeathGramplet/po/pt_PT-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Data de falecimento" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "um gramplet que mostra as datas de falecimento ordenadas" msgid "No Family Tree loaded." msgstr "Nenhuma árvore genealógica carregada." diff --git a/DateOfDeathGramplet/po/ru-local.po b/DateOfDeathGramplet/po/ru-local.po index fcda403d4..e2dc56a80 100644 --- a/DateOfDeathGramplet/po/ru-local.po +++ b/DateOfDeathGramplet/po/ru-local.po @@ -20,7 +20,7 @@ msgid "Date of Death" msgstr "Дата смерти" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "грамплет, отображающий даты смерти в отсортированном порядке" msgid "No Family Tree loaded." msgstr "Не загружено ни одного семейного древа." diff --git a/DateOfDeathGramplet/po/sk-local.po b/DateOfDeathGramplet/po/sk-local.po index 3096c4ec0..ccdbb1554 100644 --- a/DateOfDeathGramplet/po/sk-local.po +++ b/DateOfDeathGramplet/po/sk-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Dátum úmrtia" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "gramplet zobrazujúci dátumy úmrtia v zoradenom poradí" msgid "No Family Tree loaded." msgstr "Nie je načítaný žiadny rodokmeň." diff --git a/DateOfDeathGramplet/po/sv-local.po b/DateOfDeathGramplet/po/sv-local.po index 7ea19a819..f8457f961 100644 --- a/DateOfDeathGramplet/po/sv-local.po +++ b/DateOfDeathGramplet/po/sv-local.po @@ -18,7 +18,7 @@ msgid "Date of Death" msgstr "Dödsdatum" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "en gramplet som visar dödsdatum sorterade" msgid "No Family Tree loaded." msgstr "Inget släktträd laddat." diff --git a/DateOfDeathGramplet/po/tr-local.po b/DateOfDeathGramplet/po/tr-local.po index b9d400db9..7bda2f11d 100644 --- a/DateOfDeathGramplet/po/tr-local.po +++ b/DateOfDeathGramplet/po/tr-local.po @@ -21,7 +21,7 @@ msgid "Date of Death" msgstr "Ölüm tarihi" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "ölüm tarihlerini sıralı düzende gösteren bir gramplet" msgid "No Family Tree loaded." msgstr "Hiçbir aile ağacı yüklenmedi." diff --git a/DateOfDeathGramplet/po/uk-local.po b/DateOfDeathGramplet/po/uk-local.po index 3675f4f9d..34ede0e3e 100644 --- a/DateOfDeathGramplet/po/uk-local.po +++ b/DateOfDeathGramplet/po/uk-local.po @@ -19,7 +19,7 @@ msgid "Date of Death" msgstr "Дата смерті" msgid "a gramplet that displays death dates in sorted order" -msgstr "" +msgstr "грамплет, який відображає дати смерті у відсортованому порядку" msgid "No Family Tree loaded." msgstr "Не завантажено жодного родинного дерева." From 3dbc07ae584ca2526fc2f722cee599c5f8fdbc51 Mon Sep 17 00:00:00 2001 From: Javad Razavian Date: Thu, 9 Jul 2026 22:00:24 +0200 Subject: [PATCH 033/156] upd: gpr.py --- DateOfDeathGramplet/DateOfDeathGramplet.gpr.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py index 596e778c5..6badce503 100644 --- a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py +++ b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py @@ -26,9 +26,11 @@ status=STABLE, version = '1.1.0', fname="DateOfDeathGramplet.py", + authors = ["Javad Razavian"], + authors_email = ["javadr@gmail.com"], height=200, gramplet="DateOfDeathGramplet", gramps_target_version="6.1", gramplet_title=_("Date of Death"), - help_url="DateOfDeathGramplet", + help_url="Addon:DateOfDeathGramplet", ) From 6eea8bc850419ef01dc871925d8c4efcc10b8386 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Thu, 9 Jul 2026 14:06:37 -0700 Subject: [PATCH 034/156] =?UTF-8?q?Merge=20DateOfDeathGramplet=20-=20gramp?= =?UTF-8?q?let=20listing=20death=20dates=20sorted=20by=20=E2=80=A6#973?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DateOfDeathGramplet/DateOfDeathGramplet.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py index 6badce503..f088cd8fb 100644 --- a/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py +++ b/DateOfDeathGramplet/DateOfDeathGramplet.gpr.py @@ -24,7 +24,7 @@ name=_("Date of Death"), description=_("a gramplet that displays death dates in sorted order"), status=STABLE, - version = '1.1.0', + version = '1.1.1', fname="DateOfDeathGramplet.py", authors = ["Javad Razavian"], authors_email = ["javadr@gmail.com"], From 3a58376fc7d61ae3bc4d55bac3e186f7e6ed568f Mon Sep 17 00:00:00 2001 From: Eduard Ralph Date: Thu, 9 Jul 2026 03:59:36 +0200 Subject: [PATCH 035/156] lxml: don't pop a blocking dialog at import when gzip/lxml is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lxmlGramplet raised ErrorDialog(...).run() at module *import* time when gzip or lxml was unavailable. That is a blocking modal shown before any GUI action: it stalls plugin loading until dismissed, and fires (or aborts) when Gramps is imported without a display — e.g. under the CLI or a test harness, where a missing python3-lxml made the module hang or scatter dialogs. The gramplet already degrades gracefully — it falls back to xml.etree when lxml is absent — so the missing dependency is not fatal. Set the availability flags silently at import (log instead of a dialog), and show the notice in init(), when the gramplet is actually opened and the GUI is running. No .gpr.py change: the addon version is incremented automatically at publish time. Co-Authored-By: Claude Opus 4.8 (1M context) --- lxml/lxmlGramplet.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lxml/lxmlGramplet.py b/lxml/lxmlGramplet.py index 1888339c6..8f2aac75c 100644 --- a/lxml/lxmlGramplet.py +++ b/lxml/lxmlGramplet.py @@ -69,7 +69,6 @@ GZIP_OK = True except ImportError: GZIP_OK = False - ErrorDialog(_('Where is gzip?'), _('"gzip" is missing')) LOG.error('No gzip') #------------------------------------------------------------------------- @@ -91,8 +90,7 @@ LIBXSLT_VERSION = etree.LIBXSLT_VERSION except ImportError: LXML_OK = False - ErrorDialog(_('Missing python3 lxml'), _('Please, try to install "python3 lxml" package.')) - LOG.debug('No lxml') + LOG.warning('No lxml; XPATH/XSLT features are unavailable') #------------------------------------------------------------------------- # @@ -138,6 +136,16 @@ def init(self): a Run button. """ + # Report a missing optional dependency here — when the gramplet is + # actually opened and the GUI is running — rather than as a blocking + # modal dialog at module import time (which stalls plugin loading and + # can appear with no GUI, e.g. under the CLI or the test harness). + if not GZIP_OK: + ErrorDialog(_('Where is gzip?'), _('"gzip" is missing')) + if not LXML_OK: + ErrorDialog(_('Missing python3 lxml'), + _('Please, try to install "python3 lxml" package.')) + self.xmllint = "--nonet " # space at the end for additional options self.noout = True self.dropdtd = True From e4f858a16fa5ab45732d5c678e9991cf8db9fb7f Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Thu, 9 Jul 2026 16:43:46 -0700 Subject: [PATCH 036/156] Merge lxml: don't show a blocking dialog at import when lxml/gzip is missing#981 --- lxml/etreeGramplet.gpr.py | 2 +- lxml/lxmlGramplet.gpr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lxml/etreeGramplet.gpr.py b/lxml/etreeGramplet.gpr.py index 8a6e281ba..4297d3689 100644 --- a/lxml/etreeGramplet.gpr.py +++ b/lxml/etreeGramplet.gpr.py @@ -11,7 +11,7 @@ description=_("Gramplet for testing etree with Gramps XML"), status=EXPERIMENTAL, audience = DEVELOPER, - version = '1.2.5', + version = '1.2.6', gramps_target_version="6.1", include_in_listing=True, height=400, diff --git a/lxml/lxmlGramplet.gpr.py b/lxml/lxmlGramplet.gpr.py index d12d6abd8..3bd4f4d80 100644 --- a/lxml/lxmlGramplet.gpr.py +++ b/lxml/lxmlGramplet.gpr.py @@ -11,7 +11,7 @@ description=_("Gramplet for testing lxml and XSLT"), status=EXPERIMENTAL, audience = DEVELOPER, -version = '1.2.5', +version = '1.2.6', gramps_target_version="6.1", include_in_listing=True, height=400, From a6f2e2085241989c87981f82e36465339a71606f Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sat, 11 Jul 2026 08:40:08 -0700 Subject: [PATCH 037/156] PDFForms: added help URL Co-Authored-By: Claude Sonnet 5 --- PDFForms/PDFForms.gpr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PDFForms/PDFForms.gpr.py b/PDFForms/PDFForms.gpr.py index 87416aced..6d6e2b3b6 100644 --- a/PDFForms/PDFForms.gpr.py +++ b/PDFForms/PDFForms.gpr.py @@ -38,6 +38,7 @@ tool_modes=[TOOL_MODE_GUI], requires_mod=["reportlab"], depends_on=["Form Gramplet"], + help_url="Addon:PDFForms", ) register( @@ -56,4 +57,5 @@ extension="pdf", requires_mod=["pypdf"], depends_on=["Form Gramplet"], + help_url="Addon:PDFForms", ) From 6cfeb012e4fd744b0655a46263e59a03c95c95aa Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Mon, 13 Jul 2026 08:40:37 -0700 Subject: [PATCH 038/156] Merge PDFForms: added help URL #984 --- PDFForms/PDFForms.gpr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PDFForms/PDFForms.gpr.py b/PDFForms/PDFForms.gpr.py index 6d6e2b3b6..ef5f82ec0 100644 --- a/PDFForms/PDFForms.gpr.py +++ b/PDFForms/PDFForms.gpr.py @@ -26,7 +26,7 @@ "Generate blank fillable PDF forms: census/event forms or " "Ahnentafel pedigree charts." ), - version = '1.0.4', + version = '1.0.5', gramps_target_version="6.1", status=STABLE, fname="generatepdfform.py", @@ -49,7 +49,7 @@ "Import genealogy data from a PDF form. " "Send the PDF template to others to fill out and return." ), - version = '1.0.4', + version = '1.0.5', gramps_target_version="6.1", status=STABLE, fname="importpdf.py", From 80527f15862e8f16efcbe3790d0cf09f4a7e3933 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sat, 11 Jul 2026 08:10:05 -0700 Subject: [PATCH 039/156] GrampyScript: fix histogram IndexError, add editor conveniences Histogram chart's bucketing computed the bucket index from the raw value instead of its offset from min_val, so any dataset with negative numbers (or just not zero-anchored) could index past the end of the buckets list. Bucket count and index math now match, with the top edge (value == max_val) clamped into the last bucket. Editor additions: - Pasted (or loaded) tab characters are converted to 4 spaces. - Enter carries over the current line's indentation, adding a level after a trailing ':' and removing one after return/pass/break/ continue/raise. - Tab with a selection indents the touched lines; Shift+Tab dedents (with or without a selection). - Ctrl+/ toggles '#' comments on the current line or selection. - The filename label shows a '*' prefix while there are unsaved changes. Also updates a few example scripts to use the new row(person, ...) display shorthand. --- GrampyScript/GrampyScript.py | 171 ++++++++++++++++-- GrampyScript/scripts/01_list_people.gram.py | 5 +- .../scripts/07_csv_ready_report.gram.py | 6 +- .../10_find_missing_birth_dates.gram.py | 2 +- 4 files changed, 159 insertions(+), 25 deletions(-) diff --git a/GrampyScript/GrampyScript.py b/GrampyScript/GrampyScript.py index 90a83ceda..9f365634e 100644 --- a/GrampyScript/GrampyScript.py +++ b/GrampyScript/GrampyScript.py @@ -305,6 +305,7 @@ def init(self): self.liststore = None self.text_length = 0 self.chart_data = None + self.converting_tabs = False self.gui.WIDGET = self.build_gui() self.gui.get_container_widget().remove(self.gui.textview) self.gui.get_container_widget().add(self.gui.WIDGET) @@ -397,6 +398,8 @@ def build_gui(self): ) self.ebuf = UndoableBuffer() self.editor_textview.set_buffer(self.ebuf) + self.ebuf.connect_after("insert-text", self.on_insert_text_after) + self.ebuf.connect("modified-changed", self.on_modified_changed) self.keyword_tag = self.ebuf.create_tag( "keyword", foreground="blue", weight=700 ) @@ -500,8 +503,13 @@ def build_gui(self): def update_filename_label(self): name = os.path.basename(self.last_filename) if self.last_filename else _("Untitled") + if self.ebuf.get_modified(): + name = "*" + name self.filename_label.set_text(name) + def on_modified_changed(self, buffer): + self.update_filename_label() + def check_unsaved_changes(self, proceed): """ If the script has unsaved changes, ask the user whether to save, @@ -692,6 +700,21 @@ def on_buffer_changed(self, buffer): self.highlight_syntax() self.completion.on_buffer_changed() + def on_insert_text_after(self, buffer, text_iter, text, length): + if "\t" not in text or self.converting_tabs: + return + self.converting_tabs = True + try: + end_offset = text_iter.get_offset() + start_offset = end_offset - len(text) + start = buffer.get_iter_at_offset(start_offset) + end = buffer.get_iter_at_offset(end_offset) + new_text = text.replace("\t", " ") + buffer.delete(start, end) + buffer.insert(buffer.get_iter_at_offset(start_offset), new_text) + finally: + self.converting_tabs = False + def highlight_syntax(self): start_iter = self.ebuf.get_start_iter() end_iter = self.ebuf.get_end_iter() @@ -932,38 +955,147 @@ def on_editor_focus_out(self, widget, event): return False def on_key_press(self, textview, event): + keyval = event.keyval + shift_tab = keyval == Gdk.KEY_ISO_Left_Tab or ( + keyval == Gdk.KEY_Tab and (event.state & Gdk.ModifierType.SHIFT_MASK) + ) + + if shift_tab: + self.dedent_selection() + return True + + if keyval == Gdk.KEY_Tab and self.ebuf.get_has_selection(): + self.indent_selection() + return True + if self.completion.on_key_press(event): return True - if event.keyval == Gdk.KEY_Tab: + if keyval == Gdk.KEY_Tab: # buffer = textview.get_buffer() iter_ = self.ebuf.get_iter_at_mark(self.ebuf.get_insert()) self.ebuf.insert(iter_, " ") # Insert 4 spaces return True - elif event.keyval == Gdk.KEY_Return and ( - event.state & Gdk.ModifierType.MOD1_MASK - ): + elif keyval == Gdk.KEY_Return and (event.state & Gdk.ModifierType.MOD1_MASK): self.apply_button.emit("clicked") return True - elif event.keyval == Gdk.KEY_c and (event.state & Gdk.ModifierType.MOD1_MASK): + elif keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): + self.insert_auto_indent_newline() + return True + + elif keyval == Gdk.KEY_c and (event.state & Gdk.ModifierType.MOD1_MASK): self.copy_selected_text() return True - elif (Gdk.keyval_name(event.keyval) == "Z") and match_primary_mask( + elif (Gdk.keyval_name(keyval) == "Z") and match_primary_mask( event.get_state(), Gdk.ModifierType.SHIFT_MASK ): self.redo() return True - elif (Gdk.keyval_name(event.keyval) == "z") and match_primary_mask( + elif (Gdk.keyval_name(keyval) == "z") and match_primary_mask( event.get_state() ): self.undo() return True + elif keyval == Gdk.KEY_slash and match_primary_mask(event.get_state()): + self.toggle_comment_selection() + return True + return False + def compute_indent_for_new_line(self, text_before_cursor): + stripped = text_before_cursor.rstrip() + indent = re.match(r"[ \t]*", text_before_cursor).group(0).replace("\t", " ") + if stripped.endswith(":"): + indent += " " + elif re.match(r"^[ \t]*(return|pass|break|continue|raise)\b", stripped): + if indent.endswith(" "): + indent = indent[:-4] + return indent + + def insert_auto_indent_newline(self): + buf = self.ebuf + it = buf.get_iter_at_mark(buf.get_insert()) + line_start = it.copy() + line_start.set_line_offset(0) + text_before_cursor = buf.get_text(line_start, it, True) + indent = self.compute_indent_for_new_line(text_before_cursor) + buf.insert_at_cursor("\n" + indent) + + def selection_line_bounds(self): + buf = self.ebuf + if buf.get_has_selection(): + sel_start, sel_end = buf.get_selection_bounds() + else: + it = buf.get_iter_at_mark(buf.get_insert()) + sel_start = sel_end = it + start = buf.get_iter_at_line(sel_start.get_line()) + end_line = sel_end.get_line() + if end_line > sel_start.get_line() and sel_end.get_line_offset() == 0: + # A drag-selection ending at column 0 of a line usually means + # the user didn't mean to touch that line. + end_line -= 1 + end = buf.get_iter_at_line(end_line) + end.forward_to_line_end() + return start, end + + def reindent_selection(self, transform): + buf = self.ebuf + start, end = self.selection_line_bounds() + start_offset = start.get_offset() + text = buf.get_text(start, end, True) + new_text = "\n".join(transform(line) for line in text.split("\n")) + if new_text == text: + return + buf.delete(start, end) + buf.insert(buf.get_iter_at_offset(start_offset), new_text) + new_start = buf.get_iter_at_offset(start_offset) + new_end = buf.get_iter_at_offset(start_offset + len(new_text)) + buf.select_range(new_start, new_end) + + def indent_selection(self): + self.reindent_selection(lambda line: " " + line) + + def dedent_selection(self): + def dedent(line): + if line.startswith(" "): + return line[4:] + if line.startswith("\t"): + return line[1:] + return line.lstrip(" ") + + self.reindent_selection(dedent) + + def toggle_comment_selection(self): + buf = self.ebuf + start, end = self.selection_line_bounds() + text = buf.get_text(start, end, True) + code_lines = [line for line in text.split("\n") if line.strip()] + all_commented = bool(code_lines) and all( + line.lstrip().startswith("#") for line in code_lines + ) + + def comment(line): + if not line.strip(): + return line + stripped = line.lstrip(" ") + indent = line[: len(line) - len(stripped)] + return indent + "# " + stripped + + def uncomment(line): + stripped = line.lstrip(" ") + indent = line[: len(line) - len(stripped)] + if stripped.startswith("# "): + return indent + stripped[2:] + if stripped.startswith("#"): + return indent + stripped[1:] + return line + + self.reindent_selection(uncomment if all_commented else comment) + def undo(self): self.ebuf.undo() self.text_length = len(self.get_text()) @@ -1120,25 +1252,30 @@ def on_draw(self, widget, cr): min_val = min(data) if max_val == min_val: return - interval = (max_val - min_val) / self.chart_data[2] - buckets = [0] * (int(max_val / interval) + 1) + num_buckets = max(1, int(self.chart_data[2])) + interval = (max_val - min_val) / num_buckets + buckets = [0] * num_buckets for value in data: - if value > max_val: - buckets[int(max_val / interval)] += 1 - else: - buckets[int(value / interval)] += 1 + # Bucket index is the value's offset from min_val, not + # the raw value -- otherwise negative or non-zero-based + # data lands outside the buckets list. Clamp the top + # edge (value == max_val) into the last bucket rather + # than one past it. + idx = int((value - min_val) / interval) + if idx >= num_buckets: + idx = num_buckets - 1 + buckets[idx] += 1 labels = [] decimal_places = self.chart_data[3].get("decimal_places", 0) format = "%0." + str(decimal_places) + "f" - for i in range(int(max_val / interval)): - begin = format % (i * interval) - end = format % ((i + 1) * interval) + for i in range(num_buckets): + begin = format % (min_val + i * interval) + end = format % (min_val + (i + 1) * interval) if begin != end: labels.append(begin + "-" + end) else: labels.append(begin) - labels.append(format % ((i + 1) * interval,)) # Draw a bar chart with values bar_width = width / (len(buckets) * 1.5) diff --git a/GrampyScript/scripts/01_list_people.gram.py b/GrampyScript/scripts/01_list_people.gram.py index ee569eb71..4bcd8a2a6 100644 --- a/GrampyScript/scripts/01_list_people.gram.py +++ b/GrampyScript/scripts/01_list_people.gram.py @@ -6,8 +6,7 @@ for person in people(): row( - person.gramps_id, - person.name.first_name, - person.surname.surname, + person, person.gender, + person.age, ) diff --git a/GrampyScript/scripts/07_csv_ready_report.gram.py b/GrampyScript/scripts/07_csv_ready_report.gram.py index 14b83e2f1..3f5104939 100644 --- a/GrampyScript/scripts/07_csv_ready_report.gram.py +++ b/GrampyScript/scripts/07_csv_ready_report.gram.py @@ -5,15 +5,13 @@ Table tab's contents. """ -columns("ID", "Given Name", "Surname", "Gender", "Birth Year") +columns("Person", "Gender", "Birth Year") for person in people(): birth = person.birth birth_year = birth.get_date_object().get_year() if birth else "" row( - person.gramps_id, - person.name.first_name, - person.surname.surname, + person, person.gender, birth_year, ) diff --git a/GrampyScript/scripts/10_find_missing_birth_dates.gram.py b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py index eb6a21555..ac7656305 100644 --- a/GrampyScript/scripts/10_find_missing_birth_dates.gram.py +++ b/GrampyScript/scripts/10_find_missing_birth_dates.gram.py @@ -6,4 +6,4 @@ for person in people(): if not person.birth: - row(person.gramps_id, person.name.first_name, person.surname.surname) + row(person) From 2f19f7df73d223216eaf9e7e8ad5cac006575382 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Mon, 13 Jul 2026 08:43:15 -0700 Subject: [PATCH 040/156] Merge GrampyScript: fix histogram IndexError, add editor conveniences --- GrampyScript/GrampyScript.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GrampyScript/GrampyScript.gpr.py b/GrampyScript/GrampyScript.gpr.py index 79c3a54c7..34f77a285 100644 --- a/GrampyScript/GrampyScript.gpr.py +++ b/GrampyScript/GrampyScript.gpr.py @@ -23,7 +23,7 @@ name=_("Gram.py Script"), description=_("Run a special Gramps Python script"), status=STABLE, - version = '0.0.8', + version = '0.0.9', fname="GrampyScript.py", authors=["Doug Blank"], authors_email=["doug.blank@gmail.com"], From c9ae50de5268476f9e0debfd540f2dc15990f200 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Wed, 15 Jul 2026 05:47:02 -0700 Subject: [PATCH 041/156] Themes: fix TypeError on preferences_activate(initial_panel=...) GrampsPreferences.__init__ gained an initial_panel keyword so callers can open Preferences directly on a specific panel. themes_load.py monkey-patches GrampsPreferences.__init__ with MyPrefs.__init__, which didn't accept the new keyword, raising: TypeError: MyPrefs.__init__() got an unexpected keyword argument 'initial_panel' MyPrefs.__init__ now accepts initial_panel and forwards it to select_panel(), matching the core implementation. Adds a regression test. --- Themes/tests/__init__.py | 40 +++++++++ Themes/tests/test_initial_panel.py | 125 +++++++++++++++++++++++++++++ Themes/themes.py | 4 +- 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 Themes/tests/__init__.py create mode 100644 Themes/tests/test_initial_panel.py diff --git a/Themes/tests/__init__.py b/Themes/tests/__init__.py new file mode 100644 index 000000000..e8a8ad189 --- /dev/null +++ b/Themes/tests/__init__.py @@ -0,0 +1,40 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Gramps Development Team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Test package for the Themes addon. + +Pins the GTK 3 stack (Gtk + Gdk) before any test module imports +``themes``. The module imports ``gi.repository.Gdk.Screen`` directly, +which GTK 4's Gdk no longer provides -- on a host where GTK 4 is the +default GI resolution a bare import would bind the wrong version and +crash. Pinning here -- mirroring ``gramps/gen/constfunc.py`` -- applies +on every launch path, including a direct ``python3 -m unittest`` run +with no test runner. +""" + +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Gdk", "3.0") +except (ImportError, ValueError): + # No PyGObject / GTK 3 here; the test modules guard their imports + # and skip cleanly. + pass diff --git a/Themes/tests/test_initial_panel.py b/Themes/tests/test_initial_panel.py new file mode 100644 index 000000000..597bbcc3a --- /dev/null +++ b/Themes/tests/test_initial_panel.py @@ -0,0 +1,125 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Gramps Development Team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Regression test for the "initial_panel" preferences crash. + +Gramps core's ``GrampsPreferences.__init__`` (gramps/gui/configure.py) +gained an ``initial_panel`` keyword argument so callers like +``ViewManager.preferences_activate`` can open the dialog directly on a +specific panel. The Themes addon replaces ``GrampsPreferences.__init__`` +with ``MyPrefs.__init__`` (see ``themes_load.py``), which did not accept +the new keyword, raising: + + TypeError: MyPrefs.__init__() got an unexpected keyword argument + 'initial_panel' + +every time preferences were opened from a context that passes +``initial_panel`` (e.g. a "Configure" button tied to a specific panel). + +Construct ``MyPrefs`` via ``__new__`` and stub out +``ConfigureDialog.__init__``/``setup_configs`` (they build a real GTK +dialog and are irrelevant to this bug) so the test stays a fast, headless +unit test. +""" + +import os +import sys +import unittest +from unittest import mock + +# Pin Gtk to 3.0 before importing -- themes.py imports +# gi.repository.Gdk.Screen directly, which GTK 4's Gdk does not provide. +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Gdk", "3.0") +except (ImportError, ValueError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +# Make sure the addon module is importable from the parent directory. +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import themes # pylint: disable=wrong-import-position + + +def _make_prefs(): + """Return a bare MyPrefs instance (no real __init__ run yet).""" + return themes.MyPrefs.__new__(themes.MyPrefs) + + +class TestMyPrefsInitialPanel(unittest.TestCase): + """Regression guard for the initial_panel TypeError.""" + + def test_init_accepts_initial_panel_keyword(self): + """MyPrefs.__init__ must accept the initial_panel keyword that + GrampsPreferences.__init__ now takes; otherwise + ViewManager.preferences_activate's call raises TypeError.""" + import inspect + + sig = inspect.signature(themes.MyPrefs.__init__) + self.assertIn("initial_panel", sig.parameters) + self.assertIsNone(sig.parameters["initial_panel"].default) + + def test_init_selects_requested_panel(self): + """Passing initial_panel='colors' must select that panel, the + same behaviour ViewManager relies on for GrampsPreferences.""" + prefs = _make_prefs() + + def fake_configure_init(self, *_args, **_kwargs): + self.window = mock.MagicMock() + + with mock.patch.object( + themes.ConfigureDialog, "__init__", fake_configure_init + ), mock.patch.object( + themes.MyPrefs, "setup_configs", mock.MagicMock(), create=True + ), mock.patch.object( + themes.MyPrefs, "select_panel", mock.MagicMock(), create=True + ) as select_panel: + themes.MyPrefs.__init__( + prefs, mock.MagicMock(), mock.MagicMock(), initial_panel="colors" + ) + + select_panel.assert_called_once_with("colors") + + def test_init_without_initial_panel_does_not_select(self): + """The default (no initial_panel) must behave exactly as + before: no panel selection call, dialog opens on its default + page.""" + prefs = _make_prefs() + + def fake_configure_init(self, *_args, **_kwargs): + self.window = mock.MagicMock() + + with mock.patch.object( + themes.ConfigureDialog, "__init__", fake_configure_init + ), mock.patch.object( + themes.MyPrefs, "setup_configs", mock.MagicMock(), create=True + ), mock.patch.object( + themes.MyPrefs, "select_panel", mock.MagicMock(), create=True + ) as select_panel: + themes.MyPrefs.__init__(prefs, mock.MagicMock(), mock.MagicMock()) + + select_panel.assert_not_called() + + +if __name__ == "__main__": + unittest.main() diff --git a/Themes/themes.py b/Themes/themes.py index 5438fec68..2ea7ce0e1 100644 --- a/Themes/themes.py +++ b/Themes/themes.py @@ -65,7 +65,7 @@ class MyPrefs(GrampsPreferences): ''' Adds a new line of controls to the 'Colors' preferences panel. Theme, dark-theme and Font choices are added. ''' - def __init__(self, uistate, dbstate): + def __init__(self, uistate, dbstate, initial_panel=None): ''' this replaces the GrampsPreferences __init__ It includes the patching fixes and calls my version of the Theme panel ''' @@ -131,6 +131,8 @@ def __init__(self, uistate, dbstate): help_btn.connect( 'clicked', lambda x: display_help(WIKI_HELP_PAGE, WIKI_HELP_SEC)) self.setup_configs('interface.grampspreferences', 700, 450) + if initial_panel and hasattr(self, 'select_panel'): + self.select_panel(initial_panel) def add_themes_panel(self, configdialog): ''' This adds a Theme panel ''' From 0cf9795d55630c494754c7705cdfe7d3e038cc1d Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sat, 18 Jul 2026 08:42:08 -0700 Subject: [PATCH 042/156] Themes: empty tests/__init__.py, GTK pinning now handled repo-wide PR #950 added a repo-root tests/__init__.py that pins GTK/Gdk to 3.0 for the whole suite, matching the empty tests/__init__.py convention already used by every other addon. The per-addon pin here was redundant with that infrastructure. Co-Authored-By: Claude Sonnet 5 --- Themes/tests/__init__.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/Themes/tests/__init__.py b/Themes/tests/__init__.py index e8a8ad189..e69de29bb 100644 --- a/Themes/tests/__init__.py +++ b/Themes/tests/__init__.py @@ -1,40 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2026 Gramps Development Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -"""Test package for the Themes addon. - -Pins the GTK 3 stack (Gtk + Gdk) before any test module imports -``themes``. The module imports ``gi.repository.Gdk.Screen`` directly, -which GTK 4's Gdk no longer provides -- on a host where GTK 4 is the -default GI resolution a bare import would bind the wrong version and -crash. Pinning here -- mirroring ``gramps/gen/constfunc.py`` -- applies -on every launch path, including a direct ``python3 -m unittest`` run -with no test runner. -""" - -try: - import gi - - gi.require_version("Gtk", "3.0") - gi.require_version("Gdk", "3.0") -except (ImportError, ValueError): - # No PyGObject / GTK 3 here; the test modules guard their imports - # and skip cleanly. - pass From 7466c0b5a55c1d11760dd9c89bd2d28c604ec9be Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 18 Jul 2026 09:20:19 -0700 Subject: [PATCH 043/156] Merge Themes: fix TypeError on preferences_activate(initial_panel=...)#985 --- Themes/themes.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Themes/themes.gpr.py b/Themes/themes.gpr.py index 24818b0fd..4d67cc76f 100644 --- a/Themes/themes.gpr.py +++ b/Themes/themes.gpr.py @@ -31,7 +31,7 @@ "An addition to Preferences for simple Theme and Font" " adjustment. Especially useful for Windows users." ), - version = '0.0.20', + version = '0.0.21', gramps_target_version="6.1", fname="themes_load.py", authors=["Paul Culley"], From c93d9d9a6ecc7f5fce5d862478412cff3aafc831 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Wed, 15 Jul 2026 06:22:13 -0700 Subject: [PATCH 044/156] Form: drop column-size sum-to-100 warning is never used to compute layout in Form (editform.py/entrygrid.py size columns from their text content, not the XML value), and PDFForms consumes it purely as a relative weight that works with any positive total. The sum-to-100 warning added for bug 11010 was therefore firing on 78 shipped forms without there being any actual functional issue to fix. Co-Authored-By: Claude Sonnet 5 --- Form/form.py | 4 - Form/form_validator.py | 65 --------------- Form/tests/test_form_validator.py | 121 ---------------------------- Form/tests/test_integration_form.py | 46 ----------- 4 files changed, 236 deletions(-) diff --git a/Form/form.py b/Form/form.py index 940c92be1..eba50fd66 100644 --- a/Form/form.py +++ b/Form/form.py @@ -48,7 +48,6 @@ # # --------------------------------------------------------------- from form_validator import ( - get_form_warnings, validate_form_dom, validate_form_element, ) @@ -200,9 +199,6 @@ def __load_file(self, full_path): "\n".join(errors), ) - for warning in get_form_warnings(dom): - LOG.warning("In %s: %s", full_path, warning) - try: self.__load_definitions(dom) finally: diff --git a/Form/form_validator.py b/Form/form_validator.py index 5b82cd98c..4b82451de 100644 --- a/Form/form_validator.py +++ b/Form/form_validator.py @@ -153,71 +153,6 @@ def validate_form_dom(dom: xml.dom.minidom.Document) -> list[str]: return errors -def get_form_warnings(dom: xml.dom.minidom.Document) -> list[str]: - """ - Collect non-fatal warnings about a parsed form definitions DOM. - - Warnings describe likely authoring mistakes that do not prevent the - form from loading. Currently covers Gramps bug 11010's observation - that a section's ```` ```` values are expected to sum - to 100 — sections that declare explicit sizes on every column but do - not sum to 100 are reported as warnings so callers can log them - without blocking the form from loading. - - Sections without any sized columns, or with only some columns - sized, are skipped because the intent is ambiguous. - - :param dom: a parsed ``xml.dom.minidom.Document`` - :returns: a list of human-readable warning messages; empty when - nothing questionable is detected - """ - warnings: list[str] = [] - top = dom.getElementsByTagName("forms") - if not top: - return warnings - - for form in top[0].getElementsByTagName("form"): - form_id = ( - form.attributes["id"].value - if "id" in form.attributes - else "" - ) - for section in form.getElementsByTagName("section"): - role = ( - section.attributes["role"].value - if "role" in section.attributes - else "" - ) - columns = section.getElementsByTagName("column") - if not columns: - continue - - sizes: list[int] = [] - all_sized = True - for column in columns: - size_nodes = column.getElementsByTagName("size") - if not size_nodes or not size_nodes[0].childNodes: - all_sized = False - break - try: - sizes.append(int(size_nodes[0].childNodes[0].data)) - except ValueError: - all_sized = False - break - if not all_sized: - continue - - total = sum(sizes) - if total != 100: - warnings.append( - "Form '%s': section '%s' column sizes sum to %d " - "(expected 100); form will still load but column " - "widths may not render as intended" - % (form_id, role, total) - ) - return warnings - - def parse_and_validate(path: str) -> tuple[xml.dom.minidom.Document | None, list[str]]: """ Parse ``path`` as XML and validate it against the form schema. diff --git a/Form/tests/test_form_validator.py b/Form/tests/test_form_validator.py index 0efc350eb..4c9109af0 100644 --- a/Form/tests/test_form_validator.py +++ b/Form/tests/test_form_validator.py @@ -44,7 +44,6 @@ # ------------------------ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from form_validator import ( - get_form_warnings, parse_and_validate, split_family_title, validate_form_dom, @@ -394,125 +393,5 @@ def test_all_builtin_form_files_validate(self): ) -# --------------------------------------------------------------------------- -# get_form_warnings — non-fatal authoring warnings (Gramps bug 11010) -# --------------------------------------------------------------------------- -class TestGetFormWarnings(unittest.TestCase): - """ - Column sizes are expected to sum to 100. Rather than reject the - form, ``get_form_warnings`` flags suspect sections so the caller can - log them — 78 shipped definition files currently violate this rule - without breaking rendering, so escalating to an error would be - user-hostile. - """ - - def test_section_without_columns_has_no_warning(self): - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- - - """)) - self.assertEqual(get_form_warnings(dom), []) - - def test_columns_without_any_size_have_no_warning(self): - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- <_attribute>Name - <_attribute>Age -
-
-
- """)) - self.assertEqual(get_form_warnings(dom), []) - - def test_columns_summing_to_100_have_no_warning(self): - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- <_attribute>A60 - <_attribute>B40 -
-
-
- """)) - self.assertEqual(get_form_warnings(dom), []) - - def test_columns_not_summing_to_100_emit_warning(self): - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- <_attribute>A25 -
-
-
- """)) - warnings = get_form_warnings(dom) - self.assertEqual(len(warnings), 1) - self.assertIn("sum to 25", warnings[0]) - self.assertIn("F1", warnings[0]) - self.assertIn("Primary", warnings[0]) - - def test_partially_sized_columns_have_no_warning(self): - """ - When only some columns declare a ````, the author's intent - is ambiguous (mixed relative/absolute sizing) so the check is - skipped to avoid false positives. - """ - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- <_attribute>A25 - <_attribute>B -
-
-
- """)) - self.assertEqual(get_form_warnings(dom), []) - - def test_warnings_are_independent_of_errors(self): - """ - Warnings are structurally orthogonal to errors: a malformed form - still produces warnings for its well-formed sibling. - """ - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- -
-
- <_attribute>A30 -
-
- - """)) - warnings = get_form_warnings(dom) - self.assertEqual(len(warnings), 1) - self.assertIn("GOOD", warnings[0]) - - def test_multiple_misaligned_sections_all_reported(self): - dom = _dom_from_string(textwrap.dedent("""\ - -
-
- <_attribute>X40 -
-
- <_attribute>Y70 -
-
-
- """)) - warnings = get_form_warnings(dom) - self.assertEqual(len(warnings), 2) - - if __name__ == "__main__": unittest.main() diff --git a/Form/tests/test_integration_form.py b/Form/tests/test_integration_form.py index 82839c0cf..ad00f394a 100644 --- a/Form/tests/test_integration_form.py +++ b/Form/tests/test_integration_form.py @@ -43,7 +43,6 @@ # ------------------------ # Python modules # ------------------------ -import logging import os import shutil import sys @@ -259,51 +258,6 @@ def test_empty_forms_element_shows_error_dialog(self) -> None: self.assertEqual(list(instance.get_form_ids()), []) -# --------------------------------------------------------------------------- -# Column-size warnings (Gramps bug 11010 item b) — WARNING only, not errors -# --------------------------------------------------------------------------- -class TestColumnSizeWarnings(FormLoaderTestCase): - """ - Sections whose ```` sizes do not sum to 100 must be logged - as warnings only — 78 shipped forms trip this check, so escalating - to an ErrorDialog would harass users on every launch. - """ - - def test_column_size_sum_warning_is_logged_not_dialog(self) -> None: - """Column-size mismatch → WARNING log entry, no ErrorDialog.""" - self._write( - "custom.xml", - textwrap.dedent("""\ - -
-
- <_attribute>A25 -
-
-
- """), - ) - self._patch_definition_files(["custom.xml"]) - - with self.assertLogs(".FormGramplet", level=logging.WARNING) as log_ctx: - instance = self.form.Form(definition_dir=self.tmp_dir) - - self.assertFalse( - self.shown, - "column-size mismatch must not produce an ErrorDialog:\n" - + "\n".join("%s: %s" % (t, b) for t, b in self.shown), - ) - self.assertIn( - "F1", - list(instance.get_form_ids()), - "the form must still load despite the size mismatch", - ) - self.assertTrue( - any("sum to 25" in message for message in log_ctx.output), - "expected a column-size warning in the log", - ) - - # --------------------------------------------------------------------------- # Shipped files load cleanly # --------------------------------------------------------------------------- From 707cfcbbd4ed4a2d5047219331adcf0a92063375 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 18 Jul 2026 09:43:59 -0700 Subject: [PATCH 045/156] Merge Form: drop column-size sum-to-100 warning#986 --- Form/CensusCheckQuickview.gpr.py | 4 ++-- Form/formgramplet.gpr.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Form/CensusCheckQuickview.gpr.py b/Form/CensusCheckQuickview.gpr.py index be1735549..199445353 100644 --- a/Form/CensusCheckQuickview.gpr.py +++ b/Form/CensusCheckQuickview.gpr.py @@ -8,7 +8,7 @@ id = 'censuscheckquickview', name = _("CensusCheck"), description= _("Check whether any Census events are missing for a person and some of their descendents"), - version = '1.0.7', + version = '1.0.8', gramps_target_version = '6.1', status = STABLE, fname = 'CensusCheckQuickview.py', @@ -22,7 +22,7 @@ id = 'censuscheckupquickview', name = _("CensusCheckUp"), description= _("Check whether any Census events are missing for a person and some of their ancestors"), - version = '1.0.7', + version = '1.0.8', gramps_target_version = '6.1', status = STABLE, fname = 'CensusCheckUpQuickview.py', diff --git a/Form/formgramplet.gpr.py b/Form/formgramplet.gpr.py index 2acbaf2a4..f607bb3e9 100644 --- a/Form/formgramplet.gpr.py +++ b/Form/formgramplet.gpr.py @@ -31,7 +31,7 @@ name=_("Form Gramplet"), description=_("Gramplet interface for Forms"), status=STABLE, - version = '2.0.57', + version = '2.0.58', gramps_target_version="6.1", navtypes=["Person"], fname="formgramplet.py", From 35ee9acfa67b05b2e62bb51a13b5c9b220f591df Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 08:21:00 -0700 Subject: [PATCH 046/156] Remove WordleGramplet WordleGramplet generated word clouds via wordle.net, which no longer exists. The gramplet was already unstable and excluded from the listing, so there is nothing left for it to work against. --- WordleGramplet/WordleGramplet.gpr.py | 16 -- WordleGramplet/WordleGramplet.py | 172 ------------------ WordleGramplet/po/da-local.po | 30 --- WordleGramplet/po/de-local.po | 30 --- WordleGramplet/po/es-local.po | 18 -- WordleGramplet/po/fi-local.po | 32 ---- WordleGramplet/po/fr-local.po | 30 --- WordleGramplet/po/he-local.po | 31 ---- WordleGramplet/po/hr-local.po | 31 ---- WordleGramplet/po/it-local.po | 27 --- WordleGramplet/po/lt-local.po | 25 --- WordleGramplet/po/nb-local.po | 19 -- WordleGramplet/po/nl-local.po | 30 --- WordleGramplet/po/pt_PT-local.po | 30 --- WordleGramplet/po/ru-local.po | 32 ---- WordleGramplet/po/sk-local.po | 30 --- WordleGramplet/po/sv-local.po | 30 --- WordleGramplet/po/template.pot | 71 -------- WordleGramplet/po/uk-local.po | 32 ---- WordleGramplet/tests/__init__.py | 0 .../tests/test_wordlegramplet_imports.py | 91 --------- 21 files changed, 807 deletions(-) delete mode 100644 WordleGramplet/WordleGramplet.gpr.py delete mode 100644 WordleGramplet/WordleGramplet.py delete mode 100644 WordleGramplet/po/da-local.po delete mode 100644 WordleGramplet/po/de-local.po delete mode 100644 WordleGramplet/po/es-local.po delete mode 100644 WordleGramplet/po/fi-local.po delete mode 100644 WordleGramplet/po/fr-local.po delete mode 100644 WordleGramplet/po/he-local.po delete mode 100644 WordleGramplet/po/hr-local.po delete mode 100644 WordleGramplet/po/it-local.po delete mode 100644 WordleGramplet/po/lt-local.po delete mode 100644 WordleGramplet/po/nb-local.po delete mode 100755 WordleGramplet/po/nl-local.po delete mode 100644 WordleGramplet/po/pt_PT-local.po delete mode 100644 WordleGramplet/po/ru-local.po delete mode 100644 WordleGramplet/po/sk-local.po delete mode 100644 WordleGramplet/po/sv-local.po delete mode 100644 WordleGramplet/po/template.pot delete mode 100644 WordleGramplet/po/uk-local.po delete mode 100644 WordleGramplet/tests/__init__.py delete mode 100644 WordleGramplet/tests/test_wordlegramplet_imports.py diff --git a/WordleGramplet/WordleGramplet.gpr.py b/WordleGramplet/WordleGramplet.gpr.py deleted file mode 100644 index 26f0b06bb..000000000 --- a/WordleGramplet/WordleGramplet.gpr.py +++ /dev/null @@ -1,16 +0,0 @@ -register( - GRAMPLET, - id="Wordle Gramplet", - name=_("Wordle"), - status=UNSTABLE, - include_in_listing=False, - fname="WordleGramplet.py", - height=230, - gramplet="WordleGramplet", - gramplet_title=_("Wordle"), - gramps_target_version="6.1", - version = '1.0.30', - description=_("Gramplet used to make word clouds with wordle.net"), - authors=["Douglas Blank"], - authors_email=["doug.blank@gmail.com"], -) diff --git a/WordleGramplet/WordleGramplet.py b/WordleGramplet/WordleGramplet.py deleted file mode 100644 index 86f40db04..000000000 --- a/WordleGramplet/WordleGramplet.py +++ /dev/null @@ -1,172 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007-2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# $Id: WordleGramplet.py 13416 2009-10-25 20:29:45Z dsblank $ - - -#------------------------------------------------------------------------ -# -# GRAMPS modules -# -#------------------------------------------------------------------------ -from gramps.gen.plug import Gramplet -from gramps.gen.plug.report import utils as ReportUtils -from gramps.gen.const import GRAMPS_LOCALE as glocale -try: - _trans = glocale.get_addon_translator(__file__) -except ValueError: - _trans = glocale.translation -_ = _trans.gettext - -#------------------------------------------------------------------------ -# -# Constants -# -#------------------------------------------------------------------------ - -_YIELD_INTERVAL = 350 - -#------------------------------------------------------------------------ -# -# Constants -# -#------------------------------------------------------------------------ -def get_bin(n, counts, mins=8, maxs=20): - diff = maxs - mins - # based on counts (biggest to smallest) - if len(counts) > 1: - position = diff - (diff * (float(counts.index(n)) / (len(counts) - 1))) - else: - position = 0 - return int(position) + mins - -#------------------------------------------------------------------------ -# -# Gramplet class -# -#------------------------------------------------------------------------ -class WordleGramplet(Gramplet): - def init(self): - self.set_tooltip(_("Double-click surname for details")) - self.top_size = 329 # 10 # will be overwritten in load - self.set_text(_("No Family Tree loaded.")) - - def db_changed(self): - self.connect(self.dbstate.db, 'person-add', self.update) - self.connect(self.dbstate.db, 'person-delete', self.update) - self.connect(self.dbstate.db, 'person-update', self.update) - self.connect(self.dbstate.db, 'person-rebuild', self.update) - self.connect(self.dbstate.db, 'family-rebuild', self.update) - - def on_load(self): - if len(self.gui.data) > 0: - self.top_size = int(self.gui.data[0]) - - def on_save(self): - self.gui.data = [self.top_size] - - def main(self): - self.set_text(_("Processing...") + "\n") - surnames = {} - iter_people = self.dbstate.db.iter_person_handles() - self.filter = self.filter_list.get_filter() - people = self.filter.apply(self.dbstate.db, iter_people) - cnt = 0 - for person in map(self.dbstate.db.get_person_from_handle, people): - allnames = [person.get_primary_name()] + person.get_alternate_names() - allnames = set([name.get_group_name().strip() for name in allnames]) - for surname in allnames: - surnames[surname] = surnames.get(surname, 0) + 1 - cnt += 1 - if not cnt % _YIELD_INTERVAL: - yield True - - total_people = cnt - surname_sort = [] - total = 0 - - cnt = 0 - for surname in surnames: - surname_sort.append( (surnames[surname], surname) ) - total += surnames[surname] - cnt += 1 - if not cnt % _YIELD_INTERVAL: - yield True - - total_surnames = cnt - surname_sort.sort(reverse=True) - - counts = list(set([pair[0] for pair in surname_sort])) - counts.sort(reverse=True) - line = 0 - ### All done! - self.set_text("For Wordle: \n\n") - nosurname = _("[Missing]") - for (count, surname) in surname_sort: - bin = get_bin(count, counts, mins=1, maxs=self.bins.get_value()) - text = "%s: %d\n" % ((surname if surname else nosurname), bin) - self.append_text(text) - line += 1 - if line >= self.top_size: - break - self.append_text(("\n" + _("Total unique surnames") + ": %d\n") % - total_surnames) - self.append_text((_("Total people") + ": %d") % total_people, "begin") - - def build_options(self): - from gramps.gen.plug.menu import FilterOption, PersonOption, NumberOption - self.bins = NumberOption(_("Number of font sizes"), 5, 1, 10) - self.add_option(self.bins) - - self.filter_list = FilterOption(_("Filter"), 0) - self.filter_list.set_help(_("Select filter to restrict list")) - self.filter_list.connect('value-changed', self.filter_changed) - self.add_option(self.filter_list) - - self.pid_list = PersonOption(_("Filter Person")) - self.pid_list.set_help(_("The center person for the filter")) - self.pid_list.connect('value-changed', self.update_filters) - self.add_option(self.pid_list) - - self.update_filters() - - def update_filters(self): - """ - Update the filter list based on the selected person - """ - gid = self.pid_list.get_value() - try: - person = self.dbstate.db.get_person_from_gramps_id(gid) - except: - return - filters = ReportUtils.get_person_filters(person, False) - self.filter_list.set_filters(filters) - - def filter_changed(self): - """ - Handle filter change. If the filter is not specific to a person, - disable the person option - """ - filter_value = self.filter_list.get_value() - if 1 <= filter_value <= 4: - # Filters 1, 2, 3 and 4 rely on the center person - self.pid_list.set_available(True) - else: - # The rest don't - self.pid_list.set_available(False) - diff --git a/WordleGramplet/po/da-local.po b/WordleGramplet/po/da-local.po deleted file mode 100644 index 2e788ca38..000000000 --- a/WordleGramplet/po/da-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-02-25 16:12+0000\n" -"Last-Translator: Kaj Arne Mikkelsen \n" -"Language-Team: Danish \n" -"Language: da\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.10.2-dev\n" - -msgid "[Missing]" -msgstr "[Mangler]" - -msgid "Number of font sizes" -msgstr "Antal af fontstørrelser" - -msgid "Select filter to restrict list" -msgstr "Vælg filter til afgrænse listen" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet der benyttes til at danne ordskyer fra wordle.net" diff --git a/WordleGramplet/po/de-local.po b/WordleGramplet/po/de-local.po deleted file mode 100644 index da8e305f1..000000000 --- a/WordleGramplet/po/de-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: de\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-05-17 08:47+0000\n" -"Last-Translator: Mirko Leonhäuser \n" -"Language-Team: German \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.12-dev\n" - -msgid "[Missing]" -msgstr "[Fehlt]" - -msgid "Number of font sizes" -msgstr "Anzahl der Schriftgrößen" - -msgid "Select filter to restrict list" -msgstr "Filter auswählen, um Liste einzuschränken" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet verwendet, um Wortwolken mit wordle.net zu erstellen" diff --git a/WordleGramplet/po/es-local.po b/WordleGramplet/po/es-local.po deleted file mode 100644 index 7e50e7b2c..000000000 --- a/WordleGramplet/po/es-local.po +++ /dev/null @@ -1,18 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: GRAMPS 3.1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-06-14 20:06+0000\n" -"Last-Translator: Adolfo Jayme Barrientos \n" -"Language-Team: Spanish \n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.12-dev\n" - -msgid "Select filter to restrict list" -msgstr "Seleccione un filtro para restringir la lista" diff --git a/WordleGramplet/po/fi-local.po b/WordleGramplet/po/fi-local.po deleted file mode 100644 index ba6492615..000000000 --- a/WordleGramplet/po/fi-local.po +++ /dev/null @@ -1,32 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: fi\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-09-03 21:58+0000\n" -"Last-Translator: Matti Niemelä \n" -"Language-Team: Finnish \n" -"Language: fi\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.13.1-rc\n" -"Generated-By: pygettext.py 1.4\n" - -msgid "[Missing]" -msgstr "[Puuttuu]" - -msgid "Number of font sizes" -msgstr "Kirjainkokojen määrä" - -msgid "Select filter to restrict list" -msgstr "Valitse suodatin luettelon rajaamiseksi" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "" -"Grampletti, jota on käytetty sanapilvien tekemiseen wordle.net-sivustolla" diff --git a/WordleGramplet/po/fr-local.po b/WordleGramplet/po/fr-local.po deleted file mode 100644 index 361263ead..000000000 --- a/WordleGramplet/po/fr-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: trunk\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-04-11 12:50+0000\n" -"Last-Translator: jmichault \n" -"Language-Team: French \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" -"X-Generator: Weblate 5.11-dev\n" - -msgid "[Missing]" -msgstr "[Absent]" - -msgid "Number of font sizes" -msgstr "Nombre de tailles de police" - -msgid "Select filter to restrict list" -msgstr "Sélectionnez un filtre pour restreindre la liste" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet utilisé pour faire des nuages de mots avec wordle.net" diff --git a/WordleGramplet/po/he-local.po b/WordleGramplet/po/he-local.po deleted file mode 100644 index 8839d8b55..000000000 --- a/WordleGramplet/po/he-local.po +++ /dev/null @@ -1,31 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Gramps 5.2.0 – mediamerge\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2026-06-10 19:07+0000\n" -"Last-Translator: Avi Markovitz \n" -"Language-Team: Hebrew \n" -"Language: he\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " -"n % 10 == 0) ? 2 : 3));\n" -"X-Generator: Weblate 2026.6\n" - -msgid "[Missing]" -msgstr "[חסר]" - -msgid "Number of font sizes" -msgstr "מספר גדלי גופנים" - -msgid "Select filter to restrict list" -msgstr "בחירת מסנן להגבלת רשימה" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "גרמפלט ליצירת ענני מילים באמצעות wordle.net" diff --git a/WordleGramplet/po/hr-local.po b/WordleGramplet/po/hr-local.po deleted file mode 100644 index 5577ef922..000000000 --- a/WordleGramplet/po/hr-local.po +++ /dev/null @@ -1,31 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Gramps 5.x\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-03-02 14:58+0000\n" -"Last-Translator: Milo Ivir \n" -"Language-Team: Croatian \n" -"Language: hr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 5.10.3-dev\n" - -msgid "[Missing]" -msgstr "[Nedostaje]" - -msgid "Number of font sizes" -msgstr "Broj veličina fonta" - -msgid "Select filter to restrict list" -msgstr "Odaberi filtar za ograničavanje popisa" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet za izradu oblaka s riječima pomoću wordle.net" diff --git a/WordleGramplet/po/it-local.po b/WordleGramplet/po/it-local.po deleted file mode 100644 index d1b26ed32..000000000 --- a/WordleGramplet/po/it-local.po +++ /dev/null @@ -1,27 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: gramps 3\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2026-06-17 20:01+0000\n" -"Last-Translator: Paolo Zamponi \n" -"Language-Team: Italian \n" -"Language: it\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" - -msgid "[Missing]" -msgstr "[Mancante]" - -msgid "Number of font sizes" -msgstr "Totale dimensioni dei caratteri" - -msgid "Select filter to restrict list" -msgstr "Seleziona filtro per restringere gli elenchi" - -msgid "Wordle" -msgstr "Wordle" diff --git a/WordleGramplet/po/lt-local.po b/WordleGramplet/po/lt-local.po deleted file mode 100644 index 54f4c266b..000000000 --- a/WordleGramplet/po/lt-local.po +++ /dev/null @@ -1,25 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: lt\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-09-14 11:02+0000\n" -"Last-Translator: Tadas Masiulionis \n" -"Language-Team: Lithuanian \n" -"Language: lt\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"(n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 5.14-dev\n" -"Generated-By: pygettext.py 1.4\n" -"X-Poedit-Language: Lithuanian\n" -"X-Poedit-Country: LITHUANIA\n" - -msgid "Number of font sizes" -msgstr "Šrifto dydžių skaičius" - -msgid "Select filter to restrict list" -msgstr "Pasirinkite filtrą, kad apribotumėte sąrašą" diff --git a/WordleGramplet/po/nb-local.po b/WordleGramplet/po/nb-local.po deleted file mode 100644 index 0466f6603..000000000 --- a/WordleGramplet/po/nb-local.po +++ /dev/null @@ -1,19 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: nb\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2026-06-01 12:35+0000\n" -"Last-Translator: Harald Herreros \n" -"Language-Team: Norwegian Bokmål \n" -"Language: nb\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.6\n" -"Generated-By: pygettext.py 1.4\n" - -msgid "Select filter to restrict list" -msgstr "Velg filter for å begrense listen" diff --git a/WordleGramplet/po/nl-local.po b/WordleGramplet/po/nl-local.po deleted file mode 100755 index bc038627e..000000000 --- a/WordleGramplet/po/nl-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: MediaMerge 5.x\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-11-08 17:51+0000\n" -"Last-Translator: Stephan Paternotte \n" -"Language-Team: Dutch \n" -"Language: nl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.15-dev\n" - -msgid "[Missing]" -msgstr "[Ontbreekt]" - -msgid "Number of font sizes" -msgstr "Aantal lettergroottes" - -msgid "Select filter to restrict list" -msgstr "Selecteer een filter om de lijst te beperken" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet gebruikt om woordwolken te maken met wordle.net" diff --git a/WordleGramplet/po/pt_PT-local.po b/WordleGramplet/po/pt_PT-local.po deleted file mode 100644 index 33d43706b..000000000 --- a/WordleGramplet/po/pt_PT-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: gramps51\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-03-08 07:05+0000\n" -"Last-Translator: Pedro Albuquerque \n" -"Language-Team: Portuguese (Portugal) \n" -"Language: pt_PT\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.10.3-dev\n" - -msgid "[Missing]" -msgstr "(em falta)" - -msgid "Number of font sizes" -msgstr "Número de tamanhos de letra" - -msgid "Select filter to restrict list" -msgstr "Seleccione um filtro para limitar a lista" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet para construir nuvens de palavras com wordle.net" diff --git a/WordleGramplet/po/ru-local.po b/WordleGramplet/po/ru-local.po deleted file mode 100644 index 5c7c9afe0..000000000 --- a/WordleGramplet/po/ru-local.po +++ /dev/null @@ -1,32 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: gramps50\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2018-12-04 16:36+0300\n" -"Last-Translator: Ivan Komaritsyn \n" -"Language-Team: Russian\n" -"Language: ru\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Gtranslator 2.91.7\n" -"X-Poedit-Language: Russian\n" -"X-Poedit-Country: RUSSIAN FEDERATION\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" - -msgid "[Missing]" -msgstr "[Отсутствует]" - -msgid "Number of font sizes" -msgstr "Размер шрифта" - -msgid "Select filter to restrict list" -msgstr "Выберите фильтр для сокращения списка" - -msgid "Wordle" -msgstr "Облако слов" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Грамплет создающий облако слов с помощью wordle.net" diff --git a/WordleGramplet/po/sk-local.po b/WordleGramplet/po/sk-local.po deleted file mode 100644 index 770e09611..000000000 --- a/WordleGramplet/po/sk-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: GRAMPS 3.1.3\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-03-08 15:16+0000\n" -"Last-Translator: Milan \n" -"Language-Team: Slovak \n" -"Language: sk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" -"X-Generator: Weblate 5.10.3-dev\n" - -msgid "[Missing]" -msgstr "Chýbajúci]" - -msgid "Number of font sizes" -msgstr "Počet veľkostí písma" - -msgid "Select filter to restrict list" -msgstr "Vyberte filter na vymedzenie zoznamu" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet používaný na vytváranie oblakov slov s wordle.net" diff --git a/WordleGramplet/po/sv-local.po b/WordleGramplet/po/sv-local.po deleted file mode 100644 index 70112672f..000000000 --- a/WordleGramplet/po/sv-local.po +++ /dev/null @@ -1,30 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-03-06 17:38+0000\n" -"Last-Translator: Pär Ekholm \n" -"Language-Team: Swedish \n" -"Language: sv\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.10.3-dev\n" - -msgid "[Missing]" -msgstr "[Saknas]" - -msgid "Number of font sizes" -msgstr "Antal typsnittsstorlekar" - -msgid "Select filter to restrict list" -msgstr "Välj ett filter för att begränsa lista" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet för att skapa ordmoln med wordle.net" diff --git a/WordleGramplet/po/template.pot b/WordleGramplet/po/template.pot deleted file mode 100644 index 01d3d81c9..000000000 --- a/WordleGramplet/po/template.pot +++ /dev/null @@ -1,71 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: WordleGramplet/WordleGramplet.py:72 -msgid "Double-click surname for details" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:74 -msgid "No Family Tree loaded." -msgstr "" - -#: WordleGramplet/WordleGramplet.py:91 -msgid "Processing..." -msgstr "" - -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:134 -msgid "Total unique surnames" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:136 -msgid "Total people" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:143 -msgid "Filter" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:148 -msgid "Filter Person" -msgstr "" - -#: WordleGramplet/WordleGramplet.py:149 -msgid "The center person for the filter" -msgstr "" - -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "" - -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "" diff --git a/WordleGramplet/po/uk-local.po b/WordleGramplet/po/uk-local.po deleted file mode 100644 index c4a803c3a..000000000 --- a/WordleGramplet/po/uk-local.po +++ /dev/null @@ -1,32 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-16 15:10-0800\n" -"PO-Revision-Date: 2025-03-06 13:57+0000\n" -"Last-Translator: Yurii Liubymyi \n" -"Language-Team: Ukrainian \n" -"Language: uk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 5.10.3-dev\n" - -msgid "[Missing]" -msgstr "[Відсутнє]" - -msgid "Number of font sizes" -msgstr "Розмір шрифту" - -msgid "Select filter to restrict list" -msgstr "Виберіть фільтр для обмеження списку" - -msgid "Wordle" -msgstr "Wordle" - -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "" -"Gramplet використовується для створення хмар слів за допомогою wordle.net" diff --git a/WordleGramplet/tests/__init__.py b/WordleGramplet/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/WordleGramplet/tests/test_wordlegramplet_imports.py b/WordleGramplet/tests/test_wordlegramplet_imports.py deleted file mode 100644 index d1749e8c5..000000000 --- a/WordleGramplet/tests/test_wordlegramplet_imports.py +++ /dev/null @@ -1,91 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2026 Gramps Development Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" -Regression test for WordleGramplet plugin-registration imports. - -Historically ``WordleGramplet/WordleGramplet.py`` had two import -problems that broke plugin registration on Python 3: - - - ``from itertools import imap`` (``imap`` is a Py2 builtin - removed in Py3 — ``map`` is already lazy on Py3). - - ``from gen.plug import Gramplet`` and two other ``gen.plug.*`` - imports — Gramps-3 era pre-namespace paths that no longer - resolve in Gramps 5+ (the modules live under ``gramps.gen.*``). - -The addon failed plugin registration with ``cannot import name -'imap' from 'itertools'`` (the first error Python hit); once that -was fixed in isolation the next line down then raised -``ModuleNotFoundError: No module named 'gen'``. This test pins -down that the module imports cleanly end-to-end. -""" - -import os -import sys -import unittest - -# Pin Gtk to 3.0 before importing — the gramps.gen.plug import -# chain transitively touches GTK-3-only enums in gramps.gui. -# Skip cleanly if GTK 3 is not available. -try: - import gi - - gi.require_version("Gtk", "3.0") - gi.require_version("Gdk", "3.0") -except (ImportError, ValueError) as err: - raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) - -# Make sure addon modules are importable from the parent directory. -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - - -class TestWordleGrampletImports(unittest.TestCase): - """Regression: the module must import on Python 3 / Gramps 5+.""" - - def test_module_imports_and_exposes_class(self): - """WordleGramplet.py must import cleanly and expose its - ``WordleGramplet`` class as a Gramplet subclass. - - Before the migration this fails with either - ``ImportError: cannot import name 'imap' from 'itertools'`` - (on the unfixed tree) or - ``ModuleNotFoundError: No module named 'gen'`` (after the - narrow imap-only fix in this PR's earlier revision). - """ - # Addon dir and impl module share the name ``WordleGramplet``; - # under dotted-path loading the dir becomes a namespace - # package, so use the explicit submodule path. (Same trap as - # libaccess; see gramps bug 0012691 family.) - from WordleGramplet import WordleGramplet as mod - - self.assertTrue( - hasattr(mod, "WordleGramplet"), - "WordleGramplet class must be defined after import", - ) - from gramps.gen.plug import Gramplet - - self.assertTrue( - issubclass(mod.WordleGramplet, Gramplet), - "WordleGramplet must be a Gramplet subclass", - ) - - -if __name__ == "__main__": - unittest.main() From 22156970bd11893208996349b7cba1bb74d3fcbc Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 07:14:13 -0700 Subject: [PATCH 047/156] Fix crash in IsFamilyFilterMatchEvent filter rule The prepare() method built the matching event handle set in self.selected_handles but then tried to update self.events, an attribute that is never defined. This raised an AttributeError whenever the "Events of families matching a " rule was applied, breaking the filter entirely. Reported at: https://gramps.discourse.group/t/crash-of-an-event-filter-using-a-functional-family-filter/9733 --- FilterRules/isfamilyfiltermatchevent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FilterRules/isfamilyfiltermatchevent.py b/FilterRules/isfamilyfiltermatchevent.py index 25e0997ee..c533b1e23 100644 --- a/FilterRules/isfamilyfiltermatchevent.py +++ b/FilterRules/isfamilyfiltermatchevent.py @@ -92,7 +92,9 @@ def prepare(self, db: Database, user): if self.MFF: for family in db.iter_families(): if self.MFF.apply_to_one(db, family): - self.events.update([e.ref for e in family.get_event_ref_list()]) + self.selected_handles.update( + [e.ref for e in family.get_event_ref_list()] + ) def apply_to_one(self, db: Database, event: Event) -> bool: """ From 6483ea654b9c8b5eefbb97ae9ae58112eb4f33eb Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 07:22:24 -0700 Subject: [PATCH 048/156] Add regression tests for IsFamilyFilterMatchEvent Covers the AttributeError fixed in the previous commit: prepare() crashed because it updated the never-defined self.events instead of self.selected_handles. Tests build a small in-memory database with two families/events, register a custom Family filter matching one of them, and assert prepare()/apply_to_one()/GenericFilter.apply() all behave correctly without raising. --- FilterRules/tests/__init__.py | 0 .../tests/test_isfamilyfiltermatchevent.py | 178 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 FilterRules/tests/__init__.py create mode 100644 FilterRules/tests/test_isfamilyfiltermatchevent.py diff --git a/FilterRules/tests/__init__.py b/FilterRules/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/FilterRules/tests/test_isfamilyfiltermatchevent.py b/FilterRules/tests/test_isfamilyfiltermatchevent.py new file mode 100644 index 000000000..ce303bbe9 --- /dev/null +++ b/FilterRules/tests/test_isfamilyfiltermatchevent.py @@ -0,0 +1,178 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Regression tests for the ``IsFamilyFilterMatchEvent`` filter rule. + +``prepare()`` used to update ``self.events``, an attribute never +defined anywhere on the class, instead of ``self.selected_handles`` +(the attribute it initializes and that ``apply_to_one()`` actually +checks). That raised ``AttributeError: 'IsFamilyFilterMatchEvent' +object has no attribute 'events'`` whenever the "Events of families +matching a " rule was applied, crashing the filter +entirely. See: +https://gramps.discourse.group/t/crash-of-an-event-filter-using-a-functional-family-filter/9733 +""" + +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import os +import shutil +import sys +import tempfile +import unittest + +# The addon imports Gtk at module load (via +# gramps.gui.editors.filtereditor). Pin Gtk to 3.0 before any gramps +# import (mirrors what gramps.grampsapp does at startup); otherwise +# PyGObject loads GTK4 and the gramps.gui import chain crashes on +# Gtk.IconSize.MENU (a GTK3-only enum). Skip cleanly if GTK 3 / PyGObject +# aren't available. +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Gdk", "3.0") +except (ImportError, ValueError, AttributeError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +# Addon root goes on sys.path so ``FilterRules.isfamilyfiltermatchevent`` +# resolves. The ``FilterRules`` directory lacks an __init__.py, so this +# relies on Python 3 namespace packages. +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + import gramps +except ImportError as err: + raise unittest.SkipTest("gramps package not available: %s" % err) + +if "GRAMPS_RESOURCES" not in os.environ: + os.environ["GRAMPS_RESOURCES"] = os.path.dirname(os.path.dirname(gramps.__file__)) + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import make_database +from gramps.gen.lib import Event, EventRef, EventType, Family, FamilyRelType + +# CustomFilters starts out as None; it must be initialized before any +# module imports the name by value, since reload_custom_filters() +# rebinds the module-level global rather than mutating it in place. +from gramps.gen.filters import reload_custom_filters + +reload_custom_filters() +from gramps.gen.filters import CustomFilters, GenericFilterFactory +from gramps.gen.filters.rules.family import HasIdOf as FamilyHasIdOf +from gramps.cli.user import User + +from FilterRules.isfamilyfiltermatchevent import IsFamilyFilterMatchEvent + +FAMILY_FILTER_NAME = "_test_isfamilyfiltermatchevent_family_filter" + + +class IsFamilyFilterMatchEventTest(unittest.TestCase): + """Regression tests for IsFamilyFilterMatchEvent.prepare().""" + + def setUp(self): + """Build a database with two families, each with one event, and + register a custom Family filter matching only the first.""" + self.db_dir = tempfile.mkdtemp(prefix="isfamilyfiltermatchevent_") + self.db = make_database("sqlite") + self.db.load(self.db_dir) + + with DbTxn("build test db", self.db) as txn: + matched_event = Event() + matched_event.set_type(EventType(EventType.MARRIAGE)) + self.db.add_event(matched_event, txn) + self.matched_event_handle = matched_event.handle + + unmatched_event = Event() + unmatched_event.set_type(EventType(EventType.MARRIAGE)) + self.db.add_event(unmatched_event, txn) + self.unmatched_event_handle = unmatched_event.handle + + matched_family = Family() + matched_family.set_relationship(FamilyRelType(FamilyRelType.MARRIED)) + matched_ref = EventRef() + matched_ref.set_reference_handle(self.matched_event_handle) + matched_family.add_event_ref(matched_ref) + self.db.add_family(matched_family, txn) + self.matched_family_gramps_id = matched_family.gramps_id + + unmatched_family = Family() + unmatched_family.set_relationship(FamilyRelType(FamilyRelType.MARRIED)) + unmatched_ref = EventRef() + unmatched_ref.set_reference_handle(self.unmatched_event_handle) + unmatched_family.add_event_ref(unmatched_ref) + self.db.add_family(unmatched_family, txn) + + family_filter = GenericFilterFactory("Family")() + family_filter.set_name(FAMILY_FILTER_NAME) + family_filter.add_rule(FamilyHasIdOf([self.matched_family_gramps_id])) + CustomFilters.get_filters_dict("Family")[FAMILY_FILTER_NAME] = family_filter + + def tearDown(self): + del CustomFilters.get_filters_dict("Family")[FAMILY_FILTER_NAME] + self.db.close() + shutil.rmtree(self.db_dir, ignore_errors=True) + + def test_prepare_does_not_raise_attributeerror(self): + """prepare() must populate selected_handles, not crash on the + undefined self.events.""" + rule = IsFamilyFilterMatchEvent([FAMILY_FILTER_NAME]) + rule.requestprepare(self.db, User()) + self.assertEqual(rule.selected_handles, {self.matched_event_handle}) + + def test_apply_to_one_matches_only_expected_event(self): + """apply_to_one() must accept the matched family's event and + reject the unmatched family's event.""" + rule = IsFamilyFilterMatchEvent([FAMILY_FILTER_NAME]) + rule.requestprepare(self.db, User()) + matched_event = self.db.get_event_from_handle(self.matched_event_handle) + unmatched_event = self.db.get_event_from_handle(self.unmatched_event_handle) + self.assertTrue(rule.apply_to_one(self.db, matched_event)) + self.assertFalse(rule.apply_to_one(self.db, unmatched_event)) + + def test_full_filter_apply(self): + """Running the rule through a GenericFilter must return exactly + the matched family's event.""" + event_filter = GenericFilterFactory("Event")() + event_filter.add_rule(IsFamilyFilterMatchEvent([FAMILY_FILTER_NAME])) + results = set(event_filter.apply(self.db)) + self.assertEqual(results, {self.matched_event_handle}) + + def test_missing_family_filter(self): + """A rule referencing a nonexistent family filter must not + crash and must match nothing.""" + rule = IsFamilyFilterMatchEvent(["_no_such_filter_"]) + rule.requestprepare(self.db, User()) + self.assertEqual(rule.selected_handles, set()) + + +if __name__ == "__main__": + unittest.main() From f2344d6cb426d0cb52316042ef37a259ffb45076 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 07:26:21 -0700 Subject: [PATCH 049/156] Drop redundant GTK/GDK pinning in filter rule test PR #950 pins GTK/GDK to 3.0 repo-wide via tests/__init__.py, so the per-file gi.require_version() calls here were redundant. Keep only the ImportError guard for hosts without PyGObject at all. --- .../tests/test_isfamilyfiltermatchevent.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/FilterRules/tests/test_isfamilyfiltermatchevent.py b/FilterRules/tests/test_isfamilyfiltermatchevent.py index ce303bbe9..3a1f9d9f8 100644 --- a/FilterRules/tests/test_isfamilyfiltermatchevent.py +++ b/FilterRules/tests/test_isfamilyfiltermatchevent.py @@ -43,18 +43,13 @@ import unittest # The addon imports Gtk at module load (via -# gramps.gui.editors.filtereditor). Pin Gtk to 3.0 before any gramps -# import (mirrors what gramps.grampsapp does at startup); otherwise -# PyGObject loads GTK4 and the gramps.gui import chain crashes on -# Gtk.IconSize.MENU (a GTK3-only enum). Skip cleanly if GTK 3 / PyGObject -# aren't available. +# gramps.gui.editors.filtereditor). GTK/GDK are already pinned to 3.0 +# repo-wide by tests/__init__.py (PR #950); skip cleanly here if +# PyGObject isn't available at all. try: import gi - - gi.require_version("Gtk", "3.0") - gi.require_version("Gdk", "3.0") -except (ImportError, ValueError, AttributeError) as err: - raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) # Addon root goes on sys.path so ``FilterRules.isfamilyfiltermatchevent`` # resolves. The ``FilterRules`` directory lacks an __init__.py, so this From 32b0eeb627e983ad16c3b06f2bd917c33e1be893 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sun, 19 Jul 2026 16:39:00 -0700 Subject: [PATCH 050/156] Merge Fix crash in IsFamilyFilterMatchEvent filter rule#990 Also update 2 filters that were incorrectly targeted for 6.0 branch instead of 6.1 branch --- FilterRules/activepersonrule.gpr.py | 2 +- FilterRules/ageatdeath.gpr.py | 2 +- FilterRules/associationsofpersonmatch.gpr.py | 2 +- FilterRules/degreesofseparation.gpr.py | 2 +- FilterRules/familieswitheventfiltermatch.gpr.py | 2 +- FilterRules/hasrolerule.gpr.py | 4 ++-- FilterRules/hassourcefilter.gpr.py | 2 +- FilterRules/infamilyrule.gpr.py | 2 +- FilterRules/isfamilyfiltermatchevent.gpr.py | 2 +- FilterRules/isrelatedwithfiltermatch.gpr.py | 2 +- FilterRules/matcheventfilterrole.gpr.py | 4 ++-- FilterRules/matchparentoffilterfamily.gpr.py | 4 ++-- FilterRules/matchpersonfilterrole.gpr.py | 2 +- FilterRules/multipleparents.gpr.py | 2 +- FilterRules/peopleeventscount.gpr.py | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/FilterRules/activepersonrule.gpr.py b/FilterRules/activepersonrule.gpr.py index 6e0e1c65a..12cb0c391 100644 --- a/FilterRules/activepersonrule.gpr.py +++ b/FilterRules/activepersonrule.gpr.py @@ -26,7 +26,7 @@ id="ActivePerson", name=_("The active Person"), description=_("The active Person"), - version = '0.0.18', + version = '0.0.19', authors=["Paul Culley"], authors_email=["paulr2787@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/ageatdeath.gpr.py b/FilterRules/ageatdeath.gpr.py index 5409577a7..6ec50f564 100644 --- a/FilterRules/ageatdeath.gpr.py +++ b/FilterRules/ageatdeath.gpr.py @@ -24,7 +24,7 @@ id="ageatdeath", name=_("Filter people by their age at death"), description=_("Filter rule that matches people by their age at death"), - version = '1.0.19', + version = '1.0.20', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/associationsofpersonmatch.gpr.py b/FilterRules/associationsofpersonmatch.gpr.py index 2d06c54f0..e0abc867d 100644 --- a/FilterRules/associationsofpersonmatch.gpr.py +++ b/FilterRules/associationsofpersonmatch.gpr.py @@ -24,7 +24,7 @@ id="associationsofpersonmatch", name=_("Match associations of "), description=_("Match associations of "), - version = '1.0.20', + version = '1.0.21', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/degreesofseparation.gpr.py b/FilterRules/degreesofseparation.gpr.py index 5d2be8448..5b17ea6e8 100644 --- a/FilterRules/degreesofseparation.gpr.py +++ b/FilterRules/degreesofseparation.gpr.py @@ -24,7 +24,7 @@ id="degreesofseparation", name=_("People separated less than degrees of "), description=_("Filter rule that matches relatives by degrees of " "separation"), - version = '1.1.19', + version = '1.1.20', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/familieswitheventfiltermatch.gpr.py b/FilterRules/familieswitheventfiltermatch.gpr.py index 5adc41a1e..77444832f 100644 --- a/FilterRules/familieswitheventfiltermatch.gpr.py +++ b/FilterRules/familieswitheventfiltermatch.gpr.py @@ -24,7 +24,7 @@ id="familieswitheventfiltermatch", name=_("Families matching "), description=_("Matches families that are matched by an event filter"), - version = '1.0.26', + version = '1.0.27', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/hasrolerule.gpr.py b/FilterRules/hasrolerule.gpr.py index 8efbd4204..7bb7b4c10 100644 --- a/FilterRules/hasrolerule.gpr.py +++ b/FilterRules/hasrolerule.gpr.py @@ -26,7 +26,7 @@ id="HasPersonEventRole", name=_("People with events with a selected role"), description=_("Matches people with an event with a selected role"), - version = '0.0.31', + version = '0.0.32', authors=["Paul Culley"], authors_email=["paulr2787@gmail.com"], gramps_target_version="6.1", @@ -42,7 +42,7 @@ id="HasFamilyEventRole", name=_("Families with events with a selected role"), description=_("Matches families with an event with a selected role"), - version = '0.0.31', + version = '0.0.32', authors=["Paul Culley"], authors_email=["paulr2787@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/hassourcefilter.gpr.py b/FilterRules/hassourcefilter.gpr.py index 3ab286c3a..66d656521 100644 --- a/FilterRules/hassourcefilter.gpr.py +++ b/FilterRules/hassourcefilter.gpr.py @@ -27,7 +27,7 @@ id="HasSourceParameter", name=_("Source matching parameters"), description=_("Matches Sources with values containing the chosen parameters"), - version = '0.0.31', + version = '0.0.32', authors=["Dave Scheipers", "Paul Culley"], authors_email=["paulr2787@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/infamilyrule.gpr.py b/FilterRules/infamilyrule.gpr.py index 18657cb03..424964aeb 100644 --- a/FilterRules/infamilyrule.gpr.py +++ b/FilterRules/infamilyrule.gpr.py @@ -25,7 +25,7 @@ id="PersonsInFamilyFilterMatch", name=_("People who are part of families matching "), description=_("People who are part of families matching "), - version = '1.0.26', + version = '1.0.27', authors=["Matthias Kemmer", "Paul Culley"], authors_email=["matt.familienforschung@gmail.com", "paulr2787@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/isfamilyfiltermatchevent.gpr.py b/FilterRules/isfamilyfiltermatchevent.gpr.py index 2b5511199..06dca6e2a 100644 --- a/FilterRules/isfamilyfiltermatchevent.gpr.py +++ b/FilterRules/isfamilyfiltermatchevent.gpr.py @@ -24,7 +24,7 @@ id="isfamilyfiltermatchevent", name=_("Events of families matching a "), description=_("Events of families matching a "), - version = '1.0.23', + version = '1.0.24', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/isrelatedwithfiltermatch.gpr.py b/FilterRules/isrelatedwithfiltermatch.gpr.py index a3848a56b..1b3e74c3a 100644 --- a/FilterRules/isrelatedwithfiltermatch.gpr.py +++ b/FilterRules/isrelatedwithfiltermatch.gpr.py @@ -28,7 +28,7 @@ description=_( "Matches people who are related to anybody matched by " "a person filter" ), - version = '1.0.29', + version = '1.0.30', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/matcheventfilterrole.gpr.py b/FilterRules/matcheventfilterrole.gpr.py index 7b8d14511..848389ea1 100644 --- a/FilterRules/matcheventfilterrole.gpr.py +++ b/FilterRules/matcheventfilterrole.gpr.py @@ -6,10 +6,10 @@ id="MatchEventFilterRole", name=_("People from event with role"), description=_("Matches people of event filter with role"), - version = '0.0.2', + version = '0.0.3', authors=["jjdup"], authors_email=["jeremi+gramps@dupin.fdn.fr"], - gramps_target_version="6.0", + gramps_target_version="6.1", status=STABLE, fname="matcheventfilterrole.py", ruleclass="MatchesEventFilterRole", # must be rule class name diff --git a/FilterRules/matchparentoffilterfamily.gpr.py b/FilterRules/matchparentoffilterfamily.gpr.py index 9f1dfb68c..2f7206569 100644 --- a/FilterRules/matchparentoffilterfamily.gpr.py +++ b/FilterRules/matchparentoffilterfamily.gpr.py @@ -6,10 +6,10 @@ id="MatchParentOfFilterFamily", name=_("Parents of family filter"), description=_("Matches parent of family filter"), - version = '0.0.2', + version = '0.0.3', authors=["jjdup"], authors_email=["jeremi+gramps@dupin.fdn.fr"], - gramps_target_version="6.0", + gramps_target_version="6.1", status=STABLE, fname="matchparentoffilterfamily.py", ruleclass="MatchesParentOfFilterFamily", # must be rule class name diff --git a/FilterRules/matchpersonfilterrole.gpr.py b/FilterRules/matchpersonfilterrole.gpr.py index 014aef6f9..4b7e932f3 100644 --- a/FilterRules/matchpersonfilterrole.gpr.py +++ b/FilterRules/matchpersonfilterrole.gpr.py @@ -6,7 +6,7 @@ id="MatchPersonFilterRole", name=_("Events from people with role"), description=_("Matches event of people filter with role"), - version = '0.0.5', + version = '0.0.6', authors=[""], authors_email=[""], gramps_target_version="6.1", diff --git a/FilterRules/multipleparents.gpr.py b/FilterRules/multipleparents.gpr.py index e9cb4639c..dded1ed92 100644 --- a/FilterRules/multipleparents.gpr.py +++ b/FilterRules/multipleparents.gpr.py @@ -26,7 +26,7 @@ id="multipleparents", name=_("Multiple Parents Filter"), description=_("Multiple Parents Filter"), - version = '0.0.18', + version = '0.0.19', authors=["Dave Scheipers"], authors_email=["dave.scheipers@gmail.com"], gramps_target_version="6.1", diff --git a/FilterRules/peopleeventscount.gpr.py b/FilterRules/peopleeventscount.gpr.py index 112a5f215..62cfe0e27 100644 --- a/FilterRules/peopleeventscount.gpr.py +++ b/FilterRules/peopleeventscount.gpr.py @@ -24,7 +24,7 @@ id="peopleeventscount", name=_("People with of "), description=_("Matches persons which have events of given type and number."), - version = '1.0.13', + version = '1.0.14', authors=["Matthias Kemmer"], authors_email=["matt.familienforschung@gmail.com"], gramps_target_version="6.1", From 5868a5c34a352c743af98a7229655f88acd90491 Mon Sep 17 00:00:00 2001 From: "Eduard R." Date: Mon, 20 Jul 2026 02:26:24 +0200 Subject: [PATCH 051/156] Add the Addon Development manual under docs/addon-development Seventeen manual pages for addon authors - overview and getting started, tutorials per addon kind, the addon-kinds catalogue, registration fundamentals, data access, API reference, testing, debugging, troubleshooting, code analysis, internationalization, packaging, post-merge community steps, compatibility, per-release changes, normative guidelines, and roadmap - plus the diagrams they embed and a folder index. The new docs/ tree is invisible to make.py and CI: every enumeration keys on *.gpr.py or *.py globs that match nothing under docs/, verified with manifest-check and a no-op 'build docs' run. --- docs/addon-development/01-overview.md | 180 ++++++ docs/addon-development/02-tutorials.md | 594 ++++++++++++++++++ docs/addon-development/03-addon-kinds.md | 206 ++++++ docs/addon-development/04-fundamentals.md | 384 +++++++++++ docs/addon-development/05-data-access.md | 229 +++++++ docs/addon-development/06-api-reference.md | 209 ++++++ docs/addon-development/07-testing.md | 302 +++++++++ docs/addon-development/08-debug.md | 184 ++++++ docs/addon-development/09-troubleshoot.md | 213 +++++++ docs/addon-development/10-code-analysis.md | 235 +++++++ .../11-internationalization.md | 180 ++++++ docs/addon-development/12-packaging.md | 285 +++++++++ docs/addon-development/13-community.md | 84 +++ docs/addon-development/14-compatibility.md | 114 ++++ docs/addon-development/15-whats-new.md | 79 +++ docs/addon-development/16-guidelines.md | 183 ++++++ docs/addon-development/17-roadmap.md | 106 ++++ docs/addon-development/README.md | 24 + .../_media/addon-kinds-ui-map.svg | 172 +++++ docs/addon-development/_media/data-model.dot | 80 +++ docs/addon-development/_media/data-model.svg | 168 +++++ .../_media/packaging-pipeline.dot | 85 +++ .../_media/packaging-pipeline.svg | 124 ++++ .../_media/plugin-discovery.dot | 40 ++ .../_media/plugin-discovery.svg | 90 +++ 25 files changed, 4550 insertions(+) create mode 100644 docs/addon-development/01-overview.md create mode 100644 docs/addon-development/02-tutorials.md create mode 100644 docs/addon-development/03-addon-kinds.md create mode 100644 docs/addon-development/04-fundamentals.md create mode 100644 docs/addon-development/05-data-access.md create mode 100644 docs/addon-development/06-api-reference.md create mode 100644 docs/addon-development/07-testing.md create mode 100644 docs/addon-development/08-debug.md create mode 100644 docs/addon-development/09-troubleshoot.md create mode 100644 docs/addon-development/10-code-analysis.md create mode 100644 docs/addon-development/11-internationalization.md create mode 100644 docs/addon-development/12-packaging.md create mode 100644 docs/addon-development/13-community.md create mode 100644 docs/addon-development/14-compatibility.md create mode 100644 docs/addon-development/15-whats-new.md create mode 100644 docs/addon-development/16-guidelines.md create mode 100644 docs/addon-development/17-roadmap.md create mode 100644 docs/addon-development/README.md create mode 100644 docs/addon-development/_media/addon-kinds-ui-map.svg create mode 100644 docs/addon-development/_media/data-model.dot create mode 100644 docs/addon-development/_media/data-model.svg create mode 100644 docs/addon-development/_media/packaging-pipeline.dot create mode 100644 docs/addon-development/_media/packaging-pipeline.svg create mode 100644 docs/addon-development/_media/plugin-discovery.dot create mode 100644 docs/addon-development/_media/plugin-discovery.svg diff --git a/docs/addon-development/01-overview.md b/docs/addon-development/01-overview.md new file mode 100644 index 000000000..85a906284 --- /dev/null +++ b/docs/addon-development/01-overview.md @@ -0,0 +1,180 @@ +# Addon Development + +[Index](01-overview.md) · [Next →](02-tutorials.md) + +## Overview + +A Gramps **addon** extends the application without modifying core. You add a feature, ship it on your own schedule, and users install it from the in-app Plugin Manager — no fork of Gramps, no waiting on a core release to put new functionality in front of people. An addon is just a folder of Python on the plugin path, so the barrier to entry is low; the trade-off is that you build against Gramps' API and track it across versions. This is how most of Gramps' reports, tools, and gramplets are delivered, and the same door is open to you. + +Addons are discovered from the plugin directory; see [the addon list](https://gramps-project.org/wiki/index.php/6.0_Addons) for what ships today. + +This page is the **start point** for the section: first a map to every other page, then everything a first-time author needs to go from "Gramps is installed" to "my addon shows up in the menu" — anatomy, prerequisites, and a minimal working Gramplet. The normative MUST / SHOULD rules every addon is held to live in [Rules](16-guidelines.md). + +## The section at a glance + +**New to addon development?** Work through this page, then read in order — from your first loaded addon to a tested, rules-compliant one: + +*this page* → [Addon Kinds](03-addon-kinds.md) → [Fundamentals](04-fundamentals.md) → [Data access](05-data-access.md) → [Testing](07-testing.md) → [Rules](16-guidelines.md) + +**Looking for something specific?** Jump straight to it: + +| If you want to… | Go to | +|-----------------|-------| +| Install the tooling and see your first addon load | *this page, below* | +| Follow an end-to-end walkthrough for your addon kind | [Tutorials](02-tutorials.md) | +| Choose which kind of addon to build | [Addon Kinds](03-addon-kinds.md) | +| Learn the cross-cutting basics — `.gpr.py`, discovery, `_()`, logging, lifecycle | [Fundamentals](04-fundamentals.md) | +| Read from or write to the database | [Data access](05-data-access.md) | +| Look up the `gramps.gen` API an addon may import | [API Reference](06-api-reference.md) | +| Write and run tests | [Testing](07-testing.md) | +| Debug an addon that isn't behaving | [Debug](08-debug.md) | +| Diagnose a common failure mode | [Troubleshoot](09-troubleshoot.md) | +| Pass the static checks (Black, ruff) | [Code Analysis](10-code-analysis.md) | +| Translate your addon's strings | [Internationalization](11-internationalization.md) | +| Package and submit your addon | [Packaging](12-packaging.md) | +| List, announce, and support your published addon | [Community](13-community.md) | +| Port across Gramps versions | [Compatibility](14-compatibility.md) | +| See per-version changes that affect addons | [What's New](15-whats-new.md) | +| Know the rules to follow — and to cite in review | [Rules](16-guidelines.md) | +| See what's planned, or propose a change | [Roadmap](17-roadmap.md) | + +The one page to bookmark is [Rules](16-guidelines.md) — the normative MUST / SHOULD / MAY reference every addon is held to. + +## What an addon can extend (at a glance) + +Almost every part of the Gramps UI is a plugin point. The common kinds: + +| Kind | Adds | Shows up in | +|------|------|-------------| +| **Gramplet** | a lightweight widget over the current selection | Dashboard / sidebar | +| **View** | a full alternative way to browse the tree | main view area | +| **Report** | text or graphical output (PDF, HTML, ODF, …) | Reports menu | +| **Tool** | an operation over the database | Tools menu | +| **Importer / Exporter** | reading or writing an external format | File → Import / Export | +| **Quick View** | a one-call report on a selected object | right-click menus | + +…plus filter rules, sidebars, map providers, relationship calculators, citation formatters, docgen output backends, and more. The full catalogue — with the registration fields and base class each kind needs — is [Addon Kinds](03-addon-kinds.md). + +## Anatomy of an addon + +An addon is a folder under Gramps' user plugin directory — one folder per addon — holding at minimum a registration file and an implementation module: + +| File | Purpose | +|------|---------| +| `.gpr.py` | Registration: id, name, version, Gramps target, kind, entry point | +| `.py` | The implementation Gramps loads on demand | +| `po/` | Translation catalogs (optional) | +| `tests/` | Unit tests (optional, recommended) | + +At startup Gramps scans every `.gpr.py` and builds a metadata catalog from the `register(...)` call(s); the implementation module named by `fname` loads **lazily**, on first use. The consequence to remember: an error in `.gpr.py` hides the addon entirely, while an error in the implementation only surfaces when the addon is invoked. + +The registration declares the Gramps version it targets (`gramps_target_version`) — an addon on `maintenance/gramps60` expects the 6.0 API; see [Compatibility](14-compatibility.md) for cross-version concerns. + +What you build next depends on the **kind** — Gramplet, View, Report, Tool, Importer/Exporter, Quick View, and more — each adding its own registration fields and base class. Choose one in [Addon Kinds](03-addon-kinds.md); the full `.gpr.py` field reference and the discovery model are in [Fundamentals](04-fundamentals.md). + +## Prerequisites + +| Requirement | Why | +|-------------|-----| +| Gramps 6.0 installed and runnable | The target you're developing against | +| Python 3.10+ | Matches Gramps 6.0's minimum | +| A text editor or IDE | Any will do; Gramps doesn't impose one | +| Familiarity with Python imports and packages | Addons are Python modules | + +You do **not** need to build Gramps from source for addon work. Addons load from the user plugin directory and are picked up at next start. + +## Where addons live + +Each addon is a folder under Gramps' user plugin directory, one folder per addon. The exact path is platform-specific; see [the Addons page](https://gramps-project.org/wiki/index.php/6.0_Addons) for the canonical locations. The folder name must be a valid Python import name (no spaces — addons share code via `import `); it need **not** match the registration `id`, which is an independent plugin key ([Rules](16-guidelines.md) → Structure). + +On Gramps 6.0, plugin discovery does **not** follow symlinks — the addon must be physically present under the plugin path, so the development loop is copying (or `rsync`ing) from your working tree on save. + +**Changed in 6.1**: plugin discovery follows symlinks (with realpath-based dedup against symlink loops), so you can `ln -s /` into the user plugin directory and edit in place. Windows users: the 6.1 symlink test is skipped on Windows because the platform's symlink behavior is inconsistent without elevated privileges; the `rsync`/copy loop remains the safe default there. (gramps commit `9443dcbb30` on `maintenance/gramps61`.) + +## Your first addon: a minimal Gramplet + +A *Gramplet* is the lightest-weight addon kind — a sidebar widget. Two files are enough. + +### 1. Create the addon folder + +Make a folder named `HelloGramplet` under the user plugin directory. + +### 2. Add the registration file + +Save this as `HelloGramplet/HelloGramplet.gpr.py`: + +```python +register( + GRAMPLET, + id="HelloGramplet", + name=_("Hello Gramplet"), + description=_("A minimal example Gramplet"), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="hellogramplet.py", + gramplet="HelloGramplet", + gramplet_title=_("Hello"), +) +``` + +The `id` is the addon's stable identifier. `fname` is the implementation module. `gramplet` is the class inside it that Gramps will instantiate. + +### 3. Add the implementation + +Save this as `HelloGramplet/hellogramplet.py`: + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.plug import Gramplet + +_ = glocale.get_addon_translator(__file__).gettext + + +class HelloGramplet(Gramplet): + def init(self): + self.set_text(_("Hello from your first Gramplet!")) +``` + +`init()` is the construction hook — Gramps calls it once when the Gramplet is first shown. The `_ = glocale...` line binds the translation function for this module — see [Translation](#translation) below. + +### 4. Restart Gramps + +Plugin discovery happens at startup. After the restart, the new Gramplet appears under *View → Sidebar* (or the Dashboard, depending on view). + +## Reload / test cycle + +There is no hot-reload for addons. The development loop is: + +1. Edit the source. +2. Sync the change into the plugin directory (or work directly there). +3. Restart Gramps. +4. Observe. + +For faster iteration on non-GUI logic, write a `unittest`-based test alongside the addon and run it without launching Gramps — see [Testing](07-testing.md) for the conventions. + +## Translation + +Wrap every user-visible string in `_()` so it can be translated: + +```python +self.set_text(_("Hello from your first Gramplet!")) +``` + +`_` is set up differently in the two files. In `.gpr.py` it is injected by the plugin loader — just use it, never import it. In the implementation module nothing is injected: bind it explicitly at the top of the file, as the walkthrough's `hellogramplet.py` does: + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.get_addon_translator(__file__).gettext +``` + +Translation catalogues live in a per-addon `po/` directory — optional for a first experiment, required for an addon you intend to share; [Internationalization](11-internationalization.md) covers the workflow. + +## Next steps + +- [Tutorials](02-tutorials.md) — end-to-end walkthroughs per addon kind; read a similar addon's source as your second tutorial ([6.0 Addons](https://gramps-project.org/wiki/index.php/6.0_Addons) lists what exists). +- [Addon Kinds](03-addon-kinds.md) — choose the kind of addon to build; registration fields and base class per kind. +- [Fundamentals](04-fundamentals.md) — every `.gpr.py` field, the discovery model, and the lifecycle hooks the implementation overrides. +- [Testing](07-testing.md) — unit-test conventions and the `tests/` package layout. +- [Addons development](https://gramps-project.org/wiki/index.php/Addons_development) — cross-version porting notes and the wider development reference. diff --git a/docs/addon-development/02-tutorials.md b/docs/addon-development/02-tutorials.md new file mode 100644 index 000000000..60a69f3dc --- /dev/null +++ b/docs/addon-development/02-tutorials.md @@ -0,0 +1,594 @@ +# Tutorials + +[← Previous](01-overview.md) · [Index](01-overview.md) · [Next →](03-addon-kinds.md) + + + +## Overview + +End-to-end walkthroughs that take an author from empty folder to working addon. Each tutorial picks one kind, covers registration, implementation, and the reload cycle, and points at the conventions used to test it. + +Read these in order or skip to the one that matches what you're building — they're independent. They assume you've already followed [the getting-started walkthrough in 01-overview](01-overview.md#your-first-addon-a-minimal-gramplet), so we don't re-explain the user plugin directory or the restart cycle. + +| Tutorial | Kind | What it shows | +|---------------------------|---------------|---------------------------------------------------------------------| +| [A live Gramplet](#a-live-gramplet) | `GRAMPLET` | Reading the DB, refreshing on selection change, signal subscriptions | +| [A simple Tool](#a-simple-tool) | `TOOL` | The Tool / ToolOptions pair, opening a dialog, writing in a `DbTxn` | +| [A text Report](#a-text-report) | `REPORT` | The Report / ReportOptions pair, the docgen abstraction, paragraph styles | +| [A Quick View](#a-quick-view) | `QUICKVIEW` | The `run()` entry point, the Simple Access API, context-menu integration | +| [A custom filter Rule](#a-custom-filter-rule) | `RULE` | Subclassing the namespace Rule base, declaring `labels`, `apply_to_one` | + +For the conceptual map, see [01-overview](01-overview.md). For the full inventory of addon kinds and their registration constants, see [03-addon-kinds](03-addon-kinds.md). + +### A note on tutorial-style code + +The implementation modules below show the smallest code that demonstrates each kind. Two things are deliberately omitted to keep the lesson in focus, and both are **required** for shipped addons: + +- A **GPL-2.0-or-later license header** at the top of every `.py` file. Copy the header from any existing addon, or see [16-guidelines → Coding style](16-guidelines.md#coding-style). +- **Type hints** on public functions and methods (Python 3.10+ syntax — `X | None`, `list[X]`). The tutorials skip them for readability; production addons should include them per [16-guidelines → Coding style](16-guidelines.md#coding-style). + +Both are CI-checked on gramps core PRs (Black formats around the license header; `mypy` verifies the type hints); addons-source doesn't gate on them today but the rules apply to addon code regardless. + +## A live Gramplet + +**Goal.** Build a sidebar Gramplet that reads the active person from the database and shows their direct events, refreshing whenever the active person changes or the database is updated. + +The Hello Gramplet from [the overview's walkthrough](01-overview.md#your-first-addon-a-minimal-gramplet) was static text. This one is dynamic — it subscribes to signals and re-reads the DB on each update. + +### Layout + +Two files in a new folder `PersonEvents/`: + +``` +PersonEvents/ +├── PersonEvents.gpr.py +└── personevents.py +``` + +### `PersonEvents/PersonEvents.gpr.py` + +```python +register( + GRAMPLET, + id="PersonEvents", + name=_("Person Events"), + description=_("Lists the active person's direct events."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="personevents.py", + gramplet="PersonEventsGramplet", + gramplet_title=_("Events"), + height=200, + expand=True, +) +``` + +`height` and `expand` are Gramplet-specific layout fields; the rest are the same registration shape introduced in [04-fundamentals → The `.gpr.py` registration file](04-fundamentals.md#the-gprpy-registration-file). + +### `PersonEvents/personevents.py` + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.plug import Gramplet + +_ = glocale.get_addon_translator(__file__).gettext + + +class PersonEventsGramplet(Gramplet): + """List the active person's direct events; refresh on changes.""" + + def init(self): + """Build the static parts of the UI once.""" + self.set_use_markup(True) + self.set_text(_("No active person.")) + + def db_changed(self): + """Subscribe to DB signals each time the active DB changes.""" + self.connect(self.dbstate.db, "person-update", self.update) + self.connect(self.dbstate.db, "person-delete", self.update) + self.connect(self.dbstate.db, "event-update", self.update) + + def active_changed(self, handle): + """Active person changed — re-render.""" + self.update() + + def main(self): + """Pull events for the active person and render them.""" + person_handle = self.get_active("Person") + if not person_handle: + self.set_text(_("No active person.")) + return + + person = self.dbstate.db.get_person_from_handle(person_handle) + if person is None: + self.set_text(_("Active person not found.")) + return + + lines = [f"{person.gramps_id}\n"] + for event_ref in person.get_event_ref_list(): + event = self.dbstate.db.get_event_from_handle(event_ref.ref) + if event is None: + continue + date = event.get_date_object() + lines.append(f"{event.get_type()} {date}") + + self.set_text("\n".join(lines)) +``` + +### What's new vs. Hello Gramplet + +- **`db_changed()`** subscribes to DB signals. Using `self.connect(...)` (defined on `Gramplet`) instead of `self.dbstate.db.connect(...)` means Gramps tracks the subscription keys for you and disconnects them automatically when the gramplet closes or the DB swaps out. The forgotten-disconnect bug class is gone. +- **`active_changed(handle)`** is called by Gramps when the user selects a different person in the active view. The default does nothing; calling `self.update()` triggers a redraw. +- **`get_active("Person")`** returns the handle of the active person for the current view, or `None`. It honours navigation context — in a Place view it returns the active place, etc. +- **`set_use_markup(True)`** lets `set_text()` interpret Pango markup (``, ``, …); see [Gramplet textual methods](https://gramps-project.org/wiki/index.php/Gramplets_development#Textual_Output_Methods). + +### Try it + +Drop the folder into your user plugin directory (or symlink it if you're on Gramps 6.1+), restart Gramps, open a tree, and add the Gramplet from the sidebar menu. Click around different people — the displayed events should change with the selection. + +For the API surface this tutorial used (handles, refs, `iter_*`, `commit_*`), see [05-data-access](05-data-access.md). For the signal inventory, see [04-fundamentals → Signals](04-fundamentals.md#signals-addons-reacting-to-changes). + +## A simple Tool + +**Goal.** A menu-launched Tool that scans the database for people with no recorded birth date and shows the list in a dialog. + +Tools differ from gramplets in two ways: they're invoked from the Tools menu (not always visible), and they always carry an Options class — even a tool with no options must register an empty `ToolOptions` subclass. + +### Layout + +``` +MissingBirthDates/ +├── MissingBirthDates.gpr.py +└── missingbirthdates.py +``` + +### `MissingBirthDates/MissingBirthDates.gpr.py` + +```python +register( + TOOL, + id="MissingBirthDates", + name=_("Missing Birth Dates"), + description=_("Lists people with no recorded birth date."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="missingbirthdates.py", + category=TOOL_ANAL, + toolclass="MissingBirthDates", + optionclass="MissingBirthDatesOptions", + tool_modes=[TOOL_MODE_GUI], +) +``` + +`category=TOOL_ANAL` puts the tool under *Tools → Analysis and Exploration*. Other categories (`TOOL_DBPROC`, `TOOL_DBFIX`, …) are listed in [03-addon-kinds → `TOOL`](03-addon-kinds.md#tool). + +### `MissingBirthDates/missingbirthdates.py` + +```python +from gi.repository import Gtk + +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gui.dialog import OkDialog +from gramps.gui.plug import tool + +_ = glocale.get_addon_translator(__file__).gettext + + +class MissingBirthDates(tool.Tool): + """Scan the DB and report people with no recorded birth date.""" + + def __init__(self, dbstate, user, options_class, name, callback=None): + tool.Tool.__init__(self, dbstate, options_class, name) + + db = dbstate.db + missing = [] + for person in db.iter_people(): + birth_ref = person.get_birth_ref() + if birth_ref is None: + missing.append(person) + continue + event = db.get_event_from_handle(birth_ref.ref) + if event is None or event.get_date_object().is_empty(): + missing.append(person) + + if not missing: + OkDialog( + _("Missing Birth Dates"), + _("Every person has a recorded birth date."), + parent=user.uistate.window, + ) + return + + lines = [f"{p.gramps_id}: {p.get_primary_name().get_name()}" + for p in missing] + OkDialog( + _("Missing Birth Dates"), + _("{n} people with no recorded birth date:\n\n{listing}").format( + n=len(missing), + listing="\n".join(lines), + ), + parent=user.uistate.window, + ) + + +class MissingBirthDatesOptions(tool.ToolOptions): + """No options — placeholder required by the tool framework.""" +``` + +### What's new + +- **`tool.Tool.__init__(self, dbstate, options_class, name)`** — the base-class constructor. The body of `__init__` is *where the tool runs*; there's no separate `run()` method for GUI tools. +- **`MissingBirthDatesOptions`** is required even though we have no options. The `register(...)` call names it via `optionclass`, and Gramps would refuse to load the tool without it. +- **`OkDialog`** is the simplest modal report-back surface; for richer output, build a `Gtk.Dialog` directly (see `gramps/plugins/tool/dumpgenderstats.py` for the standard recipe). + +### Writing data + +If your tool *modifies* the database, all writes go inside a `DbTxn`: + +```python +from gramps.gen.db import DbTxn + +with DbTxn(_("Mark unreferenced media private"), db) as trans: + for media in db.iter_media(): + if not db.find_backlink_handles(media.handle): + media.set_privacy(True) + db.commit_media(media, trans) +``` + +The transaction message is user-visible in the Undo history; translate it. See [05-data-access → Mutating data](05-data-access.md#mutating-data) for the full pattern. + +### Try it + +After restart, the tool appears in *Tools → Analysis and Exploration → Missing Birth Dates*. Run it on `example.gramps` to see the dialog. + +## A text Report + +**Goal.** A simple text report that summarises the database — number of people, number of families, count by gender. Produces the same content through PDF, HTML, ODF, or any other docgen-supported format. + +Reports are the heaviest of the everyday addon kinds. Three pieces work together: + +- A **Report** class that knows how to walk the data and emit it as paragraphs and tables, leaving format details to the docgen. +- An **Options** class that defines user-adjustable options and the paragraph / font styles. +- A **registration** call wiring both into the menu. + +### Layout + +``` +DbSummary/ +├── DbSummary.gpr.py +└── dbsummary.py +``` + +### `DbSummary/DbSummary.gpr.py` + +```python +register( + REPORT, + id="DbSummary", + name=_("Database Summary"), + description=_("Produces a short summary of the family tree."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="dbsummary.py", + category=CATEGORY_TEXT, + require_active=False, + reportclass="DbSummaryReport", + optionclass="DbSummaryOptions", + report_modes=[REPORT_MODE_GUI, REPORT_MODE_CLI], +) +``` + +`category=CATEGORY_TEXT` makes this a text report — Gramps will offer the user the text-output document backends (PDF, ODF, plain text, …). `require_active=False` because a database summary doesn't need a specific active person. + +### `DbSummary/dbsummary.py` + +```python +from collections import Counter + +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.lib import Person +from gramps.gen.plug import docgen +from gramps.gen.plug.report import MenuReportOptions, Report +from gramps.gen.plug.report import stdoptions + +_ = glocale.get_addon_translator(__file__).gettext + + +class DbSummaryReport(Report): + """A text report summarising the database.""" + + def __init__(self, database, options_class, user): + Report.__init__(self, database, options_class, user) + self.set_locale( + options_class.menu.get_option_by_name("trans").get_value() + ) + self._count() + + def _count(self): + """Walk every Person and tally.""" + self.total = 0 + gender_counts = Counter() + surnames = Counter() + for person in self.database.iter_people(): + self.total += 1 + gender_counts[person.get_gender()] += 1 + primary = person.get_primary_name() + surnames[primary.get_primary_surname().get_surname()] += 1 + self.gender_counts = gender_counts + self.unique_surnames = len(surnames) + self.top_surname = ( + surnames.most_common(1)[0] if surnames else (_("(none)"), 0) + ) + + def write_report(self): + """Emit paragraphs into self.doc.""" + self.doc.start_paragraph("DBS-Title") + self.doc.write_text(self._("Database Summary")) + self.doc.end_paragraph() + + self.doc.start_paragraph("DBS-Normal") + self.doc.write_text( + self._("Total persons: {n}").format(n=self.total)) + self.doc.end_paragraph() + + for gender_code, label in [ + (Person.MALE, _("Males")), + (Person.FEMALE, _("Females")), + (Person.UNKNOWN, _("Unknown gender")), + ]: + self.doc.start_paragraph("DBS-Normal") + self.doc.write_text( + self._("{label}: {n}").format( + label=label, + n=self.gender_counts.get(gender_code, 0), + ) + ) + self.doc.end_paragraph() + + self.doc.start_paragraph("DBS-Normal") + self.doc.write_text( + self._("Unique surnames: {n}").format(n=self.unique_surnames) + ) + self.doc.end_paragraph() + + self.doc.start_paragraph("DBS-Normal") + self.doc.write_text( + self._("Most common surname: {name} ({n})").format( + name=self.top_surname[0], n=self.top_surname[1]) + ) + self.doc.end_paragraph() + + +class DbSummaryOptions(MenuReportOptions): + """Options form and default styles for DbSummaryReport.""" + + def add_menu_options(self, menu): + category = _("Report Options") + stdoptions.add_localization_option(menu, category) + + def make_default_style(self, default_style): + # Title style: 18 pt bold sans-serif, centred, header level 1. + font = docgen.FontStyle() + font.set_size(18) + font.set_type_face(docgen.FONT_SANS_SERIF) + font.set_bold(True) + para = docgen.ParagraphStyle() + para.set_header_level(1) + para.set_alignment(docgen.PARA_ALIGN_CENTER) + para.set_font(font) + para.set_description(_("Style used for the title of the report.")) + default_style.add_paragraph_style("DBS-Title", para) + + # Body style: 12 pt serif. + font = docgen.FontStyle() + font.set_size(12) + font.set_type_face(docgen.FONT_SERIF) + para = docgen.ParagraphStyle() + para.set_font(font) + para.set_description(_("Style used for normal report text.")) + default_style.add_paragraph_style("DBS-Normal", para) +``` + +### What's new + +- **Two classes, one file.** The `register()` call points `reportclass` at the Report and `optionclass` at the Options. +- **`self.doc` is not a file.** It's the live document — a docgen backend instance. The report writes paragraphs and text into it regardless of output format. +- **Paragraph style names are prefixed.** Use `DBS-` (or any short prefix unique to your report) on every style name. Reports get composed into Book reports, where every style name has to be unique across all contributing reports. +- **Localisation is explicit.** `stdoptions.add_localization_option` adds the standard "report locale" option to the form; the report reads it with `self.set_locale(...)` and uses `self._()` for strings that should follow the *report's* chosen locale rather than the UI locale. The leading underscore in `self._` is intentional. +- **`MenuReportOptions`** is the convenient base; for a no-options report, override only `add_menu_options` (to add the locale option) and `make_default_style` (to define paragraph styles). + +### Try it + +After restart, the report appears in *Reports → Text Reports → Database Summary*. Run it through any text document backend (PDF, ODF, plain text) to see the same content reformatted by each. + +For more on the docgen abstraction, see [Report Generation](https://gramps-project.org/wiki/index.php/Report_Generation). For richer reports (tables, multiple paragraph levels, graphical reports using `CATEGORY_DRAW`), see [Report API](https://gramps-project.org/wiki/index.php/Report_API). + +## A Quick View + +**Goal.** A right-click action on a person that lists their siblings — brothers and sisters from every family they're a child in. + +Quick Views are the shortest path to a usable report. There's no class to subclass and no options form to maintain — just a `run()` function and the registration. They're written against the **Simple Access API** (`SimpleAccess`, `SimpleDoc`), which trades some power for very little code. + +### Layout + +``` +Siblings/ +├── Siblings.gpr.py +└── siblings.py +``` + +### `Siblings/Siblings.gpr.py` + +```python +register( + QUICKVIEW, + id="Siblings", + name=_("Siblings"), + description=_("Lists the active person's siblings."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="siblings.py", + category=CATEGORY_QR_PERSON, + runfunc="run", +) +``` + +`category=CATEGORY_QR_PERSON` puts the entry on the person context menu. `runfunc="run"` names the function Gramps calls. The full set of categories is listed in [03-addon-kinds → `QUICKVIEW`](03-addon-kinds.md#quickview). + +### `Siblings/siblings.py` + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.simple import SimpleAccess, SimpleDoc +from gramps.gui.plug.quick import QuickTable + +_ = glocale.get_addon_translator(__file__).gettext + + +def run(database, document, person): + """Display all siblings of the given person.""" + sdb = SimpleAccess(database) + sdoc = SimpleDoc(document) + + sdoc.title(_("Siblings of {name}").format(name=sdb.name(person))) + sdoc.paragraph("") + + table = QuickTable(sdb) + table.columns(_("Person"), _("Gender"), _("Birth date")) + + own_gid = sdb.gid(person) + for family in sdb.child_in(person): + for child in sdb.children(family): + if sdb.gid(child) == own_gid: + continue + table.row(child, sdb.gender(child), sdb.birth_date(child)) + document.has_data = True + + table.write(sdoc) +``` + +### What's new + +- **`run(database, document, person)`** — the function signature is fixed by the QuickView kind. The third argument is the *selected object* of the category (`CATEGORY_QR_PERSON` → person, `CATEGORY_QR_FAMILY` → family, …). +- **`SimpleAccess`** is the high-level read interface — `sdb.children(family)`, `sdb.birth_date(person)`, `sdb.name(person)`. It hides handle dereferencing, refs, and date formatting. For the full surface, see [Simple Access API](https://gramps-project.org/wiki/index.php/Simple_Access_API). +- **`SimpleDoc`** is the matching write interface — `sdoc.title(...)`, `sdoc.paragraph(...)`, `sdoc.header1(...)`. +- **`QuickTable`** builds an interactive table where each row links back to a real Gramps object — clicking a person opens that person. +- **`document.has_data = True`** tells Gramps the report produced output. When all rows are filtered out, the empty-state path triggers instead. + +### Try it + +After restart, right-click any person in the People view or the person editor. *Quick View → Siblings* appears in the menu. The result opens in a Quick View window; clicking a row in the table opens that person. + +For Quick Views that don't fit the Simple Access surface, you can reach for the full DB API — see [05-data-access](05-data-access.md). The two are complementary; a complex Quick View can use both. + +## A custom filter Rule + +**Goal.** A filter rule "Has at least N children" that the user can add to a custom person filter from the Filter Editor. + +Filter rules are the smallest addon kind by line count and the one with the most reuse: a single rule, written once, drops into every filter the user composes — search, narrative website, reports, gramplets that accept a filter. + +### Layout + +``` +HasNChildren/ +├── HasNChildren.gpr.py +└── hasnchildren.py +``` + +### `HasNChildren/HasNChildren.gpr.py` + +```python +register( + RULE, + id="HasNChildren", + name=_("People with at least N children"), + description=_("Matches people who have at least N children."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="hasnchildren.py", + ruleclass="HasNChildren", + namespace="Person", +) +``` + +`namespace="Person"` says this rule applies to people. The other namespaces (`Family`, `Event`, `Place`, `Source`, `Citation`, `Repository`, `Media`, `Note`) get their own rules — Gramps' filter editor groups rules by namespace. + +### `HasNChildren/hasnchildren.py` + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.filters.rules import Rule + +_ = glocale.get_addon_translator(__file__).gettext + + +class HasNChildren(Rule): + """Matches people with at least N children.""" + + labels = [_("Minimum count:")] + name = _("People with at least N children") + category = _("Family filters") + description = _("Matches people with at least N children") + + def apply_to_one(self, db, person): + try: + minimum = int(self.list[0]) + except (TypeError, ValueError): + return False + total = 0 + for family_handle in person.get_family_handle_list(): + family = db.get_family_from_handle(family_handle) + if family is None: + continue + total += len(family.get_child_ref_list()) + if total >= minimum: + return True + return False +``` + +### What's new + +- **`labels`** declares the user-prompted arguments — one entry per text box in the filter-editor dialog. The user's typed values arrive on `self.list` in the same order. Always parse defensively; `self.list[0]` is a string straight from the GUI. +- **`name`, `category`, `description`** are class attributes — Gramps reads them off the class (no instance needed) when building the Add Rule dialog. `category` is the section the rule appears under in that dialog. +- **`apply_to_one(self, db, person)`** is the per-object hook. It returns `True` for a match, `False` for a non-match. Gramps calls it for every person in the namespace when applying the filter. On Gramps 6.0 the API is `apply_to_one`; older releases used `apply` (see [gramps/gen/filters/rules/_rule.py:162](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/filters/rules/_rule.py#L162)). + +### Optional hooks + +- **`prepare(self, db, user)`** — called once before the rule is applied to many objects, on demand. Use it to precompute lookup tables when `apply_to_one` would otherwise repeat expensive work. Pair with `reset()` to release memory afterwards. +- **`allow_regex = True`** — opt the first label into regex input. + +### Try it + +After restart, *Edit → Person Filter Editor → Add → Add Rule* shows "People with at least N children" under *Family filters*. The user types a number in the "Minimum count" field; the rule does the rest. + +The rule is also visible from gramplets like *Filter Gramplet* and as an input to any tool or report that accepts a person filter — no extra work needed; rules are uniform across the framework. + +## See also + +- [01-overview → Your first addon](01-overview.md#your-first-addon-a-minimal-gramplet) — the prerequisites and the development loop these tutorials build on. +- [03-addon-kinds](03-addon-kinds.md) — registration details per kind. +- [04-fundamentals](04-fundamentals.md) — `.gpr.py` fields, signals, `requires_mod`, lifecycle hooks. +- [05-data-access](05-data-access.md) — the DB API patterns used by these tutorials. +- [07-testing](07-testing.md) — how to test what you just wrote without launching Gramps. +- [Report API](https://gramps-project.org/wiki/index.php/Report_API), [Report Generation](https://gramps-project.org/wiki/index.php/Report_Generation) — depth on the docgen abstraction. +- [Simple Access API](https://gramps-project.org/wiki/index.php/Simple_Access_API) — the Quick View read surface. diff --git a/docs/addon-development/03-addon-kinds.md b/docs/addon-development/03-addon-kinds.md new file mode 100644 index 000000000..9abcfa056 --- /dev/null +++ b/docs/addon-development/03-addon-kinds.md @@ -0,0 +1,206 @@ +# Addon Kinds + +[← Previous](02-tutorials.md) · [Index](01-overview.md) · [Next →](04-fundamentals.md) + + + +## Overview + +Gramps doesn't have one "addon" shape — it has 14 of them, each registered with a different `register(KIND, …)` constant and each plugged in at a different extension point. This page is the index over all of them, with the registration constant, the UI location, the base class to subclass, and a pointer onward. Use it to answer the first question every prospective addon author asks: **which kind of thing am I writing?** + +Source of truth for the constants: [`gramps/gen/plug/_pluginreg.py`](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/plug/_pluginreg.py). + +![Fig. 1 — Where each addon kind plugs into the Gramps UI. Menu-anchored kinds (REPORT, TOOL, IMPORT/EXPORT) appear inline with the menu item that hosts them; panel-anchored kinds (SIDEBAR, VIEW, GRAMPLET, QUICKVIEW, MAPSERVICE, RULE) carry callouts to their surface. The six kinds with no direct UI surface — DOCGEN, DATABASE, RELCALC, THUMBNAILER, CITE, GENERAL — are listed separately. Schematic; relative positions match Gramps 6.0's default layout but are not pixel-accurate.](_media/addon-kinds-ui-map.svg) + +## Kinds at a glance + +| Constant | Where it shows up | Typical use | +|----------------|----------------------------------|------------------------------------------------------------------------------| +| `GRAMPLET` | Dashboard, sidebar, bottombar | Lightweight widget over the current selection | +| `VIEW` | Main view area | A full alternative way to browse the tree | +| `REPORT` | Reports menu | Text / graphical output (PDF, HTML, ODF, …) using the docgen interface | +| `TOOL` | Tools menu | Operates on the database, optionally writing inside a transaction | +| `IMPORT` | File → Import | Reads an external format into the tree | +| `EXPORT` | File → Export | Writes the tree to an external format | +| `DOCGEN` | Report output backends | Adds a new output format / paper backend used by reports | +| `QUICKVIEW` | Right-click context menus | Single-call short report on a selected object (formerly `QUICKREPORT`) | +| `SIDEBAR` | Sidebar navigator | Adds a new sidebar category | +| `MAPSERVICE` | Geography view | Adds a new map tile provider | +| `RELCALC` | Relationships view | Per-locale relationship calculator | +| `RULE` | Filter editor | Adds a new filter rule for an object type | +| `DATABASE` | New tree backend selection | Adds support for another database backend | +| `THUMBNAILER` | Media handling | Adds a thumbnail generator for an additional media format | +| `CITE` | Source citations | Adds a citation formatter style | +| `GENERAL` | (varies) | Catch-all for libraries / pluggable categories (`WEBSTUFF`, `Filters`, …) | + +`QUICKREPORT` is the legacy name for `QUICKVIEW`; the integer constant is identical (`gramps/gen/plug/_pluginreg.py` line 83). New addons use `QUICKVIEW`; existing ones continue to work. + +## Per-kind notes + +The notes below cover the kinds an addon author is likely to write. Kinds with deeper conventions get their own section; the rest are summarised in one paragraph each. For full attribute lists per kind, the authoritative reference is the `expand_*` functions in `_pluginreg.py`. + +### `GRAMPLET` + +**Where it shows up:** docked in the Dashboard, sidebar, or bottombar of any view; can be detached into a floating window. + +**Base class:** subclass `gramps.gen.plug.Gramplet`. Override `init()` (constructor hook, runs once), `main()` (re-run on update), `db_changed()` (called when the active database changes), and `active_changed()` (called when the active person / family / etc. changes). + +**Minimum-viable shape:** + +```python +from gramps.gen.plug import Gramplet + +class MyGramplet(Gramplet): + def init(self): + self.set_text(_("Hello")) +``` + +**Registration:** see [01-overview → Add the registration file](01-overview.md#2-add-the-registration-file) for the full call. Required Gramplet-specific fields are `gramplet` (the class name) and `gramplet_title` (the user-visible tab title). + +**Tutorial:** [02-tutorials → A live Gramplet](02-tutorials.md#a-live-gramplet). + +### `REPORT` + +**Where it shows up:** Reports menu, organised by category. + +**Base class:** subclass `gramps.gen.plug.report.Report`. Override `write_report()` to emit content. Pair with an options class that subclasses `gramps.gen.plug.report.MenuReportOptions` and overrides `add_menu_options()` (to define user-adjustable options) and `make_default_style()` (to define paragraph and font styles). + +**Categories** (`_pluginreg.py` L141–L149): `CATEGORY_TEXT`, `CATEGORY_DRAW`, `CATEGORY_CODE`, `CATEGORY_WEB`, `CATEGORY_BOOK`, `CATEGORY_GRAPHVIZ`, `CATEGORY_TREE`. Text and Draw reports go through the docgen abstraction, so the same report can emit PDF / HTML / ODF without per-format code. + +**Report modes** (`report_modes` field): `REPORT_MODE_GUI` (dialog-driven), `REPORT_MODE_BKI` (book item), `REPORT_MODE_CLI` (command line). Most addons combine GUI + CLI. + +**Tutorial:** [02-tutorials → A text Report](02-tutorials.md#a-text-report). + +### `TOOL` + +**Where it shows up:** Tools menu, optionally categorised. + +**Base class:** subclass a class from `gramps.gui.plug.tool` (typically `Tool` or `BatchTool`). Override the constructor — Gramps passes `(dbstate, user, options_class, name, callback=None)`. Tools that mutate the database **must** do so inside a `DbTxn`. + +**Categories** (`_pluginreg.py` L154–L159): `TOOL_DEBUG`, `TOOL_ANAL`, `TOOL_DBPROC`, `TOOL_DBFIX`, `TOOL_REVCTL`, `TOOL_UTILS`. Choose the one that matches what the tool actually does — `TOOL_DBFIX` for repairs, `TOOL_ANAL` for read-only analysis, `TOOL_UTILS` for generic utilities. + +**Tool modes** (`tool_modes` field, `_pluginreg.py` L183–L184): `TOOL_MODE_GUI` and `TOOL_MODE_CLI`. A pure-data tool should support both so a power user can scriptit. + +**Tutorial:** [02-tutorials → A simple Tool](02-tutorials.md#a-simple-tool). + +### `QUICKVIEW` + +**Where it shows up:** right-click context menus on the selected object in views and editors. + +**Entry point:** a `run(database, document, person_or_family_or_…)` function declared in the implementation module and pointed to by the `runfunc` field. No class subclassing required. + +**Categories** (`_pluginreg.py` L163–L174): `CATEGORY_QR_PERSON`, `CATEGORY_QR_FAMILY`, `CATEGORY_QR_EVENT`, `CATEGORY_QR_SOURCE`, `CATEGORY_QR_PLACE`, `CATEGORY_QR_REPOSITORY`, `CATEGORY_QR_NOTE`, `CATEGORY_QR_DATE`, `CATEGORY_QR_MEDIA`, `CATEGORY_QR_CITATION`, `CATEGORY_QR_SOURCE_OR_CITATION`, `CATEGORY_QR_MISC`. The category determines which context menu the entry appears in. + +Quick Views are deliberately the shortest path to a usable report — written against the `gramps.gen.simple` API (`SimpleAccess`, `SimpleDoc`), they hide most of the docgen complexity. Reach for a full `REPORT` only when you need styles, paragraph layout, or multiple output formats. + +**Tutorial:** [02-tutorials → A Quick View](02-tutorials.md#a-quick-view). + +### `RULE` + +**Where it shows up:** the Add Rule dialog when the user composes a custom filter from the Filter Editor; available wherever filters are. + +**Base class:** subclass the right rule base from `gramps.gen.filters.rules` — pick the namespace-specific base (`gramps.gen.filters.rules.person.Rule`, `…family.Rule`, etc.) that matches the object type your rule applies to. Set the class attributes `name`, `description`, `category`, and `labels` (the user-prompted arguments); implement `apply(db, obj)` to return `True` / `False`. + +**Tutorial:** [02-tutorials → A custom filter Rule](02-tutorials.md#a-custom-filter-rule). + +### `VIEW` + +**Where it shows up:** the main view area; available from the navigator once registered. + +**Base class:** subclass an appropriate view from `gramps.gui.views` (`NavigationView`, `ListView`, `PageView`). Views are the heaviest addon kind — they own the entire display surface and the keyboard / mouse interaction. Most addons should reach for `GRAMPLET` instead and only graduate to `VIEW` when the gramplet outgrows its container. + +**Live examples:** `CombinedView`, `LifeLineChartView`, `QuiltView` — read one before writing your own. + +### `IMPORT` / `EXPORT` + +**Where they show up:** File → Import / Export, with the new format appearing in the format dropdown. + +**Entry point:** a module-level function. Importers receive `(database, filename, user)`; exporters receive `(database, filename, error_dialog, option_box, callback)` (signatures vary slightly by Gramps minor; the safest move is to read a live importer/exporter and copy the shape). + +**Live examples:** the GEDCOM (`gramps/plugins/importer/importgedcom.py`, `…/exporter/exportgedcom.py`) and JSON importers/exporters in core are the canonical references. + +### `DOCGEN` + +**Where it shows up:** as a new output format in any Report's options dialog; not user-launched on its own. + +**Base class:** subclass `gramps.gen.plug.docgen.BaseDoc` (or the text/draw subclasses depending on what kind of output you generate). A DocGen implements the *primitives* — paragraphs, tables, drawing commands — that the abstract Report classes call into. Authors usually only write a new DocGen to add a new output format (e.g. a new word-processor file type); it's a relatively rare addon kind. + +### `SIDEBAR` + +**Where it shows up:** the navigator on the left of the main window; each `SIDEBAR` plugin adds one category. + +**Base class:** subclass `gramps.gui.sidebar.Sidebar`. Core categories (People, Families, Events, …) are themselves implemented this way, so the canonical examples ship in core under `gramps/gui/sidebar/`. + +### `MAPSERVICE` + +**Where it shows up:** the Geography views' map-source dropdown. + +**Base class:** subclass `gramps.plugins.lib.maps.osmgps.MapService` and implement the URL / tile-fetch protocol for your provider. Pure tile adapters — no UI changes — so most are very small. + +### `RELCALC` + +**Where it shows up:** wherever Gramps computes a relationship string (Relationships view, person editor, reports). One `RELCALC` plugin per locale. + +**Base class:** subclass `gramps.gen.relationship.RelationshipCalculator`. The base class supplies all the English-language logic; subclasses override the localised strings and any kinship rules specific to the culture being modelled. + +### `DATABASE` + +**Where it shows up:** the database-backend dropdown in tree creation. + +Adds a fully alternative storage backend implementing the `DbReadBase` / `DbWriteBase` interfaces. By far the heaviest kind — the only current in-tree examples are the BSDDB and SQLite backends themselves. Treat the existence of this kind as "yes, it is possible," not "you should consider writing one." + +### `THUMBNAILER` + +**Where it shows up:** wherever Gramps generates a media thumbnail. + +Adds a generator for one additional media format. Pure-function shape: input file → thumbnail image. Use this when a media type Gramps recognises doesn't have a working thumbnailer in your environment. + +### `CITE` + +**Where it shows up:** the citation style chooser in source / citation editors and reports. + +Adds an alternative citation formatter (Chicago, MLA, Evidence Explained, …). Implements the formatting protocol expected by the source / citation code; cite an existing core formatter (`gramps/plugins/cite/`) for the exact shape on the branch you're targeting. + +### `GENERAL` + +**Where it shows up:** nowhere directly — `GENERAL` is the escape hatch for plugin code that doesn't fit any other kind. Two main uses: + +- **Shared libraries** — code reused across multiple addons. Set `load_on_reg=True` and the file gets imported at startup; everything in it becomes importable to other plugins as `import `. The `libwebconnect` addon, depended on by every Web Connect Pack, is the archetype. +- **Pluggable categories** — `GENERAL` plugins can declare a `category` string; other code can then ask the plugin manager for all `GENERAL` plugins of category `WEBSTUFF` (CSS stylesheets for the narrative website report) or `Filters` (filter-rule providers). New categories are rare; the published ones are documented in [addons-development](https://gramps-project.org/wiki/index.php/Addons_development#Registered_GENERAL_Categories). + +The category `WEBSTUFF` is the one most addon authors meet: addons that ship a stylesheet for the narrative website register as `GENERAL, category="WEBSTUFF"` and the website report picks them up automatically. + +**The plugin-data API.** Three registration fields drive the category machinery. A plugin contributes data either statically (`data = [...]` right in the `.gpr.py`) or dynamically — if the implementation module defines a function named `load_on_reg(dbstate, uistate, plugin)`, Gramps calls it at registration and its return value becomes the plugin's data. A `process = "function_name"` field names a function applied over the accumulated data when a consumer asks for it. Consumers query by category through the plugin manager: + +```python +from gramps.gui.pluginmanager import GuiPluginManager + +plugman = GuiPluginManager.get_instance() +plugman.get_plugin_data("WEBSTUFF") # all data from WEBSTUFF plugins +plugman.process_plugin_data("WEBSTUFF") # same, run through the process function +``` + +Note there is **no automatic loading** of `GENERAL` plugins beyond this: without `load_on_reg=True` the module sits unimported until something imports it explicitly. + +## Multiple kinds in one addon + +A single `.gpr.py` can call `register(...)` more than once. The classic case is a report that also registers a Quick View entry for the same underlying logic (`gramps/plugins/quickview/all_events.py` does this for events). Each `register()` call is independent; only the addon folder / `id` and the implementation file(s) are shared. + +## See also + +- [01-overview](01-overview.md) — what an addon is, file roles, first Gramplet end-to-end. +- [02-tutorials](02-tutorials.md) — per-kind walkthroughs. +- [04-fundamentals](04-fundamentals.md) — the cross-cutting concepts every kind relies on, including [the provided environment](04-fundamentals.md#the-provided-environment) every kind inherits from Gramps' startup. +- [`gramps/gen/plug/_pluginreg.py`](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/plug/_pluginreg.py) — the authoritative definition of all the constants and `expand_*` attribute lists per kind. +- [6.0 Addons](https://gramps-project.org/wiki/index.php/6.0_Addons) — the canonical catalogue of what already exists per kind; reading a similar addon's source is your fastest second tutorial. diff --git a/docs/addon-development/04-fundamentals.md b/docs/addon-development/04-fundamentals.md new file mode 100644 index 000000000..74639de4f --- /dev/null +++ b/docs/addon-development/04-fundamentals.md @@ -0,0 +1,384 @@ +# Fundamentals + +[← Previous](03-addon-kinds.md) · [Index](01-overview.md) · [Next →](05-data-access.md) + + + +## Overview + +The cross-cutting concerns every addon author hits regardless of which kind they're building. If something in a kind-specific page assumes a piece of background, it's described here. + +![Fig. 1 — Plugin discovery and load sequence. Gramps scans the plugin directory at startup, executes each `register()` call into a metadata-only catalog, and loads the implementation module lazily when the user first invokes the addon.](_media/plugin-discovery.svg) + +Note that the catalog → invoke arrow is dashed: addon implementation modules are *not* loaded at startup. The `.gpr.py` is what runs during discovery; the `fname` module only loads on first use. This is why a registration-time error blocks the whole addon from appearing, but a runtime error in the implementation only surfaces when the user triggers it. + +## The `.gpr.py` registration file + +Every addon ships exactly one `.gpr.py` per folder, executed at startup by Gramps' plugin scanner. Its single job is to call `register(...)` one or more times, declaring the addon's *metadata* — what kind it is, what version of Gramps it targets, which implementation module to load on demand. + +The general shape: + +```python +register( + GRAMPLET, # kind (see 03-addon-kinds) + id="HelloGramplet", # stable identifier — folder name + name=_("Hello Gramplet"), # user-visible label + description=_("A minimal example"), + version="1.0.0", # addon version, X.Y.Z + gramps_target_version="6.0", # which Gramps minor this targets + status=STABLE, # STABLE / BETA / EXPERIMENTAL / UNSTABLE + fname="hellogramplet.py", # implementation module + # kind-specific fields go here + gramplet="HelloGramplet", + gramplet_title=_("Hello"), +) +``` + +### Fields every kind needs + +| Field | Meaning | +|-------------------------|----------------------------------------------------------------------------------| +| `id` | Stable plugin key, unique across addons; need **not** match the folder name | +| `name` | User-visible label, translatable | +| `version` | Addon version, dotted `X.Y.Z` | +| `gramps_target_version` | The Gramps minor this targets, e.g. `"6.0"` | +| `status` | `STABLE`, `BETA`, `EXPERIMENTAL`, or `UNSTABLE` | +| `fname` | The implementation module Gramps loads on first use | + +### Fields most kinds want + +- `description` — shown in the Plugin Manager tooltip. +- `authors`, `authors_email` — credit and contact, both lists. +- `maintainers`, `maintainers_email` — only set if different from authors. +- `help_url` — wiki page name; Gramps prepends the base URL and may add a language extension. Don't wrap in `_()` unless you actually want per-language wiki pages. +- `audience` — `EVERYONE` (default), `EXPERT`, or `DEVELOPER`; filters visibility in the Plugin Manager. The constants live at `_pluginreg.py:75-77` — note `EVERYONE`, not `ALL` (an outdated wiki page documents `ALL`; the code has only ever used `EVERYONE`). + +### Kind-specific fields + +Every kind adds its own. A few examples: + +- `GRAMPLET` adds `gramplet` (class or function name), `gramplet_title`, `height`, `expand`, `navtypes`, `force_update`. +- `REPORT` adds `reportclass`, `optionclass`, `category`, `report_modes`, `require_active`. +- `TOOL` adds `toolclass`, `optionclass`, `category`, `tool_modes`. +- `QUICKVIEW` adds `runfunc`, `category`. + +[03-addon-kinds](03-addon-kinds.md) lists the kind-specific fields per kind. The authoritative reference is the `expand_*` helpers in [`_pluginreg.py`](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/plug/_pluginreg.py). + +### Multiple registrations per file + +A single `.gpr.py` may call `register(...)` more than once — for example a report that also exposes a quick view, or two related gramplets sharing one implementation module. Each call is independent metadata. + +## Plugin discovery + +Gramps walks the plugin path at startup, executes every `.gpr.py` it finds, and builds an in-memory catalog from each `register()` call. The implementation modules pointed to by `fname` are **not** loaded at this point — they're imported lazily on first invocation. This split matters for diagnostics: + +- A `SyntaxError` or import failure in `.gpr.py` makes the addon disappear entirely from menus — the catalog never got an entry for it. +- A failure inside the implementation module surfaces only when the user triggers the addon, with a traceback in the Plugin Manager and the log window. + +### The plugin path + +Plugin folders are searched under each path Gramps was configured to scan — typically the system-wide plugin dir plus the per-user plugin dir. The per-user dir is the safe one to develop in; system locations generally need elevated permissions and shouldn't be edited directly. The exact paths are platform-specific; [the Addons page](https://gramps-project.org/wiki/index.php/6.0_Addons) lists them. + +### Symlinks + +Plugin discovery's symlink handling changed between 6.0 and 6.1: + +- **Gramps 6.0** — symlinks are **not** followed. An addon symlinked in is invisible. Development loop: copy/`rsync` from working tree on save. +- **Gramps 6.1+** — symlinks **are** followed, with realpath-based dedup so cycles terminate. Symlinking the working tree into the user plugin dir works in place. (Gramps commit [`9443dcbb30`](https://github.com/gramps-project/gramps/commit/9443dcbb30) on `maintenance/gramps61`.) The symlink test is skipped on Windows because the platform's symlink behaviour is inconsistent without elevated privileges; on Windows, a physical copy remains the safe approach even on 6.1+. + +Concrete sync recipes live in [01-overview → Where addons live](01-overview.md#where-addons-live). + +## Names Gramps injects into `.gpr.py` + +The `.gpr.py` runs in a scope where several names are *pre-populated* by the plugin loader. You **must not import** them; Gramps puts them there and an `import` masks them with stale bindings. + +| Injected name | Source | +|------------------------------------------------------------------|-------------------------------------| +| `register` | the loader itself | +| `_` (and `ngettext`) | the addon's local translation | +| Kind constants — `GRAMPLET`, `REPORT`, `TOOL`, … | `gramps.gen.plug._pluginreg` | +| Status constants — `STABLE`, `BETA`, `EXPERIMENTAL`, `UNSTABLE` | `_pluginreg.py:62-65` | +| Audience constants — `EVERYONE`, `EXPERT`, `DEVELOPER` | `_pluginreg.py:75-77` | +| Report category constants — `CATEGORY_TEXT`, `CATEGORY_DRAW`, … | `_pluginreg.py:141-149` | +| Tool category constants — `TOOL_DBPROC`, `TOOL_DBFIX`, … | `_pluginreg.py:154-159` | +| Quick View category constants — `CATEGORY_QR_PERSON`, … | `_pluginreg.py:163-174` | +| Report mode constants — `REPORT_MODE_GUI`, `REPORT_MODE_BKI`, … | `_pluginreg.py` | + +In the implementation module, none of these are injected — the rules are normal Python. Import what you need from `gramps.gen.*` there. + +## The provided environment + +The injected names are one half of what Gramps hands an addon; the other half is process-global. An addon — whatever its kind — is a **guest in Gramps' process**: before the first plugin loads, Gramps' startup (`gramps/grampsapp.py`, with `gramps/gen/utils/grampslocale.py` and `gramps/gen/plug/_manager.py`) has already configured the state the addon runs inside. Each item below is a real temptation, because setting it up yourself is exactly what makes a module work *standalone* — and each one either collides with, or silently hijacks, the running application. The rule is uniform: **the app provides this state at runtime; the test root provides it under test; addon modules touch none of it.** + +| Gramps sets up at startup | The tempting mistake | An addon instead | Documented in | +|---------------------------|----------------------|------------------|---------------| +| **GI version pins** — `gi.require_version("Gtk", "3.0")` / `("Gdk", "3.0")` before any plugin loads | Pinning in the addon module or a test file so bare `unittest` imports work | Never pin; addons-source's repo-root `tests/__init__.py` carries the pins (PR 950) | [07-testing → The GTK-pin contract](07-testing.md#the-gtk-pin-contract) | +| **Locale & translation** — `locale.setlocale(LC_ALL, "")`, gettext domain binding, ICU collators | `gettext.install()` (overwrites the builtin `_` app-wide), `locale.setlocale` for date/number formatting, `locale.strcoll` for sorting | Use the injected `_` in `.gpr.py`; `glocale.get_addon_translator(__file__)` in modules; `glocale.sort_key` for collation | [Translation](#translation) below, [11-internationalization](11-internationalization.md) | +| **Root logger & error reporting** — WARNING-level root logger with stderr/file handlers; in the GUI, `GtkHandler` turns ERROR into the error-report dialog | `logging.basicConfig(...)` or `getLogger().setLevel(...)` in a module or test to "see output" — duplicates handlers and reroutes the error dialog for the whole app | A named module-level logger only: `LOG = logging.getLogger(".MyAddon")` | [Logging](#logging) below, [08-debug → Default log levels](08-debug.md#default-log-levels) | +| **`sys.path`** — the plugin manager adds your addon dir *transiently* at import and pops it after | `sys.path.insert(0, os.path.dirname(__file__))` at module level so sibling/vendored imports resolve standalone — inside Gramps the entry is permanent and global, and a generic `utils.py` shadows every other addon's | Rely on the loader's import semantics; under test, the invocation through the package root provides the path | [09-troubleshoot → Imports and namespace traps](09-troubleshoot.md#imports-and-python-namespace-traps) | +| **The GTK main loop & global GTK state** — one `Gtk.main()` loop, the icon theme, screen-wide CSS, `Gtk.Settings` | `Gtk.main()` / `Gtk.main_quit()` around your own dialog (the standalone-script habit); installing app-wide CSS providers or retheming globally | Use Gramps' dialog and windowing machinery; style your own widgets, never the screen | [16-guidelines → Runtime](16-guidelines.md#runtime) | +| **`sys.excepthook`** — logs unhandled exceptions and, on a `HandleError`, flags the DB for check-and-repair at next start | Installing your own hook for "nicer" error handling — disables crash reporting *and* the DB-repair flag app-wide | Let exceptions propagate; log expected failures through your module logger | [08-debug](08-debug.md) | +| **Environment & user paths** — `GRAMPS_RESOURCES`, `PANGOCAIRO_BACKEND` (Windows), the user config/plugin directories | `os.environ[...] = ...` in module or test-module code; computing Gramps paths from `__file__` | Paths come from `gramps.gen.const`; environment setup belongs to the harness (test root, CI) | [07-testing → Running tests locally](07-testing.md#running-tests-locally) | + +The test-side mirror of this table — the repository-root `tests/__init__.py` reproducing the slice of this environment modules under test need, with test runs going through the repo root so it loads — is the contract in [07-testing → The GTK-pin contract](07-testing.md#the-gtk-pin-contract). + +## Translation + +Every user-visible string in the `.gpr.py` and in the implementation goes through `_()`. The function is set up differently in the two files because the `.gpr.py` runs in the injected-name scope. + +**In `.gpr.py`**: just use `_()`. The loader has already wired it. + +```python +register( + GRAMPLET, + id="HelloGramplet", + name=_("Hello"), + description=_("A minimal example"), + ... +) +``` + +**In the implementation module**: opt into the addon's own translation catalog at the top of the file, then use `_()` normally. + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.get_addon_translator(__file__).gettext +``` + +This binds `_` to translations stored in the addon's own `po/` folder rather than Gramps' core catalog. Without this line, `_()` falls back to the core catalog and your addon-specific strings stay in English regardless of UI language. + +### Plurals + +Use `ngettext(singular, plural, n)` whenever a number is being formatted into a string. Languages with non-trivial plural rules (Russian, Polish, …) need both forms to render correctly. + +```python +msg = ngettext("{n} match", "{n} matches", n).format(n=n) +``` + +### Disambiguating contexts + +When the same English word translates differently in different contexts, add a context hint. Gramps' `_()` accepts `_(msg, context)`; the older `pgettext(context, msg)` form also works but the comma form is preferred because the source remains readable as plain English. + +```python +_("Source", "citation") # vs. _("Source", "person attribute") +``` + +## Logging + +Use a module-level logger; never use `print()` for diagnostics. + +```python +import logging + +LOG = logging.getLogger(".".join(__name__.split(".")[-2:])) +# or simply: +LOG = logging.getLogger(__name__) + +LOG.debug("Reached the interesting branch with n=%d", n) +LOG.warning("Skipping malformed event %s", event.gramps_id) +``` + +Log output flows into: + +- **The Gramps log window** (Help → Log) — visible to the user. +- **stderr** when Gramps is launched with `--debug` or with `GRAMPS_DEBUG=1` set. + +See [08-debug](08-debug.md) for how to enable debug levels per logger. + +## Lifecycle hooks + +Every kind has its own entry points; the shape varies, but the pattern is consistent: a small number of named methods that Gramps calls at specific moments, and you override the ones you need. + +### Gramplets + +Subclass `gramps.gen.plug.Gramplet`. The hooks Gramps calls: + +| Method | When | +|----------------------|-----------------------------------------------------------------------------------| +| `init(self)` | Once, on first show. Build the UI here. Don't read the DB yet — it may not be open. | +| `db_changed(self)` | When the active database changes. Reconnect any signals you wired on the old DB. | +| `active_changed(self, handle)` | When the active person / family / etc. changes. Default is to call `update()`. | +| `main(self)` | The work itself. May be a generator — `yield True` to keep going, `yield False` to stop. | +| `update(self)` | Don't override. Calls `main()` for you; you call `update()` to schedule a redraw. | +| `on_load(self)` / `on_save(self)` | When the gramplet's persistent data is loaded / saved. | + +Inside the class, `self.dbstate.db` is your live database, `self.uistate` is the GUI state. See [05-data-access](05-data-access.md) for what you can do with `self.dbstate.db`. + +### Reports + +Subclass `gramps.gen.plug.report.Report`. The constructor receives `(database, options_class, user)`. Override `write_report()` — that's the single hook Gramps calls. Everything else is plumbing you initialise in `__init__`. + +### Tools + +Subclass from `gramps.gui.plug.tool`. The constructor receives `(dbstate, user, options_class, name, callback=None)` and does the work inline (there's no separate `run()` for non-CLI tools). For CLI mode, `tool_modes=[TOOL_MODE_CLI]` triggers a different entry path. + +### Quick Views + +Plain function: `run(database, document, person_or_family_or_…)`. No class to subclass. Point `runfunc` at it in the registration. + +### Importers / Exporters + +Plain function pointed to by `fname` + the kind's entry-point field. Signature varies by kind and minor; reading a live importer/exporter is the most reliable way to lock down the exact shape on your target branch. + +## Signals: addons reacting to changes + +Gramps' database and UI emit *signals* when state changes. Addons that need to stay in sync — gramplets that refresh on data changes, views that follow the selection — `connect()` to those signals. + +### The minimal pattern + +```python +key = self.dbstate.db.connect("person-update", self.cb_person_changed) +# … later, in teardown … +self.dbstate.db.disconnect(key) +``` + +`connect()` returns an opaque key; pass it to `disconnect()` when the addon shuts down or the database changes. Forgetting to disconnect leaves stale callbacks pointing into freed objects and crashes Gramps sooner or later. + +### The signals that matter most + +| Source | Signal | When | +|------------------------|--------------------------------------------------|--------------------------------------------------------------------| +| `dbstate.db` | `person-add`, `family-add`, `event-add`, … | One object added. Arg: list of handles. | +| `dbstate.db` | `person-update`, `family-update`, … | One object updated. Arg: list of handles. | +| `dbstate.db` | `person-delete`, `family-delete`, … | One object deleted. Arg: list of handles. | +| `dbstate.db` | `person-rebuild`, `family-rebuild`, … | Mass change (import, db repair). No args. | +| `dbstate.db` | `home-person-changed` | Home person changed. No args. | +| `dbstate` | `database-changed` | Active database swapped. Arg: the new db. | +| `dbstate` | `no-database` | No db is open. | +| `uistate` | `nameformat-changed`, `filter-name-changed`, … | Various UI preferences. | +| view's history | `active-changed` | Selected object changed. Arg: the new handle. | + +Pattern: `person-update` / `family-update` / etc. fire one *after* a transaction commits, with a *list* of affected handles. They never fire mid-transaction, so callbacks can safely re-read the DB. + +### Subscribing to "anything changed" + +A common gramplet pattern is "redraw on any structural change to the tree", typically done by wiring `db_changed`: + +```python +def db_changed(self): + self.dbstate.db.connect("person-add", self.update) + self.dbstate.db.connect("person-delete", self.update) + self.dbstate.db.connect("person-update", self.update) + self.dbstate.db.connect("family-add", self.update) + self.dbstate.db.connect("family-delete", self.update) + self.dbstate.db.connect("family-update", self.update) +``` + +For complex subscriptions across many object types, the `CallbackManager` in `gramps.gen.utils.callman` is a higher-level filter that lets you register dictionaries of `{signal: handler}` and tracks keys for `disconnect_all()` on teardown. See [Signals and callbacks](https://gramps-project.org/wiki/index.php/Signals_and_Callbacks) for the full inventory. + +### Signal ordering + +Signals are deferred until a transaction commits and are emitted in a specific order: deletes first, then adds, then updates; within each phase, by object type in the order persons → families → sources → events → media → places → repositories → notes → tags → citations. This deterministic order matters when a single transaction touches related objects (a family merge deletes one family and updates another plus its members); a handler that re-reads the DB on `person-delete` will see a consistent state. + +## Reading and writing the database + +The DB API is covered in depth in [05-data-access](05-data-access.md). The rule worth stating here, where every addon meets it: + +- **Reading** is unrestricted. Any addon may read freely from `self.dbstate.db`. +- **Writing** goes through a transaction. Always: + + ```python + with DbTxn(_("Description for Undo history"), db) as trans: + person = db.get_person_from_handle(handle) + person.set_privacy(True) + db.commit_person(person, trans) + ``` + +The transaction message is user-visible in the Undo history; translate it. + +## Declaring dependencies + +Addons may need Python packages or system tools that aren't part of Gramps' core dependencies. Declare these in the registration so the plugin manager can surface a clear "missing X" message instead of a generic import failure. + +### `requires_mod` — Python modules + +```python +requires_mod = ["PIL", "lxml"] +``` + +Uses the **importable** module name (what you `import`), **not** the PyPI distribution name. PIL not Pillow, lxml fine either way (matches), yaml not PyYAML. Verify before you push: + +```python +from importlib.util import find_spec +assert find_spec("PIL") is not None +``` + +A mismatch shows up the first time the addon's tests run against a clean install — the import fails. Always verify the name with `find_spec` before publishing. + +### `requires_gi` — GObject Introspection bindings + +```python +requires_gi = [("GExiv2", "0.10")] +``` + +A list of `(namespace, version)` tuples. The user has to install these through their OS package manager; Gramps cannot install GI bindings. The version pin must match what your code actually imports — and on gramps61 the version handling for GExiv2 was rewritten (addons-source PR 829), so a `requires_gi` pinned for one branch isn't guaranteed correct on the other. Verify against the target branch's related code before assuming a cherry-pick is correct. + +### `requires_exe` — Executables on PATH + +```python +requires_exe = ["graphviz", "dot"] +``` + +External binaries the user must have installed. Gramps checks PATH for them and surfaces a missing-dependency message. + +### `depends_on` — Other addons + +```python +depends_on = ["libwebconnect"] +``` + +Other addons that must load first. The plugin manager resolves these automatically when the user installs your addon. Circular dependencies break the load and disable the addon — the loader chooses safety over guessing. + +## Configuration and persistent settings + +For settings that should survive between sessions, Gramps' configuration manager handles the file I/O and migration; you only declare the keys. + +```python +from gramps.gen.config import config as configman + +config = configman.register_manager("my_addon") +config.register("section.key1", default_value) +config.register("section.key2", another_default) +config.load() # read existing settings file, if any +config.save() # write defaults out if the file didn't exist +``` + +`config.get("section.key1")` and `config.set("section.key1", value)` read and write at runtime. Gramplets persist via the lifecycle hook: + +```python +def on_save(self): + config.save() +``` + +The settings file lives in the addon's plugin folder by default. For a system-wide config (rare): + +```python +config = configman.register_manager("my_addon", use_config_path=True) +``` + +Other code — another addon, a repro script — can read an addon's settings without re-registering the keys, via `get_manager`: + +```python +from gramps.gen.config import config as configman + +config = configman.get_manager("my_addon") +value = config.get("section.key1") +``` + +## See also + +- [01-overview → Your first addon](01-overview.md#your-first-addon-a-minimal-gramplet) — the first end-to-end Gramplet putting these concepts together. +- [03-addon-kinds](03-addon-kinds.md) — what each kind adds to the registration shape described here. +- [05-data-access](05-data-access.md) — the DB API surface. +- [06-api-reference](06-api-reference.md) — the curated `gramps.gen.*` surface that addons may import. +- [09-troubleshoot](09-troubleshoot.md) — what failure modes look like when one of these conventions is off. +- [Signals and Callbacks](https://gramps-project.org/wiki/index.php/Signals_and_Callbacks) — the standalone wiki page covering signals and the `CallbackManager` in more depth. diff --git a/docs/addon-development/05-data-access.md b/docs/addon-development/05-data-access.md new file mode 100644 index 000000000..3697c9439 --- /dev/null +++ b/docs/addon-development/05-data-access.md @@ -0,0 +1,229 @@ +# Data access + +[← Previous](04-fundamentals.md) · [Index](01-overview.md) · [Next →](06-api-reference.md) + + + +## Overview + +Every addon that does anything useful with a family tree reads or writes through the **database API** — the `DbReadBase` / `DbWriteBase` interface implemented by Gramps' database backends (BSDDB historically, SQLite from 6.0 onward). + +You don't instantiate a database yourself. The plugin loader hands you a `DbState` object; the live database is `dbstate.db`. Everything below is methods on that handle. + +```python +db = dbstate.db # this is your entry point +``` + +The same `db` works for read-only addons (reports, gramplets, quick views) and for tools that mutate data. Mutation goes through transactions; see [Mutating data](#mutating-data) below. + +## Identifying objects: handles vs Gramps IDs + +Every primary object (Person, Family, Event, Place, Source, Citation, Repository, Media, Note, Tag) has **two identifiers**: + +| Identifier | Stable | Format | Used for | +|------------|--------|--------|----------| +| **Handle** | Yes (internal, never reused) | 32-char hex string | Cross-references in the database | +| **Gramps ID** | User-renameable | `I0001`, `F0001`, `E0001`, ... | User-visible labels and external interop | + +**Rule of thumb:** use handles inside your code; show Gramps IDs to the user. Handles never change; Gramps IDs do (the user can edit them, the "Reorder Gramps IDs" tool can rewrite them in bulk). + +```python +# Right: traverse by handle +person = db.get_person_from_handle(handle) + +# Right: show the user a Gramps ID +print(f"Working on {person.gramps_id}") + +# Wrong: traverse by Gramps ID (works, but slower and breaks under reorder) +person = db.get_person_from_gramps_id("I0001") +``` + +Each object class has both lookup methods (`get__from_handle` and `get__from_gramps_id`); see [06-api-reference](06-api-reference.md) for the full list. + +## Reading: one object at a time + +The fastest pattern, when you have a handle in hand: + +```python +person = db.get_person_from_handle(person_handle) +family = db.get_family_from_handle(family_handle) +event = db.get_event_from_handle(event_handle) +``` + +Each returns `None` if the handle isn't in the database (deleted, broken reference). Always guard: + +```python +person = db.get_person_from_handle(handle) +if person is None: + return # silently skip, or raise a HandleError if the caller expects one +``` + +For `HandleError` and friends, import from `gramps.gen.errors`. + +## Reading: iterating all objects + +For reports and surveys you'll want every object of a given type. The database exposes one generator per object class: + +```python +for person in db.iter_people(): + ... + +for family in db.iter_families(): + ... +``` + +These are **generators**, not lists — they stream through the database without loading everything into memory. Don't call `list(db.iter_people())` on a 50,000-person tree unless you have a reason. + +To iterate just the handles (cheaper when you only need to count or filter): + +```python +for handle in db.iter_person_handles(): + ... +``` + +Counts come without iteration: + +```python +db.get_number_of_people() +db.get_number_of_families() +db.get_number_of_events() +``` + +## Following references + +![Fig. 1 — Gramps primary objects and the most-traversed relationships. Edges labelled `Ref` (e.g. `EventRef`, `CitationRef`, `MediaRef`) go through a ref object that carries metadata such as the role or relationship; bare-labelled edges are direct handle references. Notes and Tags can be attached to any primary object and are omitted to keep arrows readable. Reverse traversals — "who refers to this object?" — go through `db.find_backlink_handles()` instead of these forward links; see Backlinks below.](_media/data-model.svg) + +Most addons don't visit objects in isolation — they follow the relationships between them. Gramps' object model exposes references as **handle lists** on the parent object. + +Person → families they're a parent in: + +```python +for family_handle in person.get_family_handle_list(): + family = db.get_family_from_handle(family_handle) + ... +``` + +Person → events: + +```python +for ref in person.get_event_ref_list(): + event = db.get_event_from_handle(ref.ref) + role = ref.get_role() + ... +``` + +`event_ref` carries more than the handle — also the role (Primary, Witness, etc.) and any private flag. Read the ref, then dereference if you need the event itself. + +Family → children: + +```python +for child_ref in family.get_child_ref_list(): + child = db.get_person_from_handle(child_ref.ref) + ... +``` + +The full handle-list / ref-list inventory per object class lives in the [Gramps API docs](https://gramps-project.org/wiki/index.php/Gramps_6.0_Developer_Reference); see also [06-api-reference](06-api-reference.md) for the addon-facing subset. + +## Backlinks: who refers to this object? + +The forward direction (person → events they participated in) lives on the object. The reverse direction (event → people who participated in it) lives on the database: + +```python +for (obj_type, obj_handle) in db.find_backlink_handles(event.handle): + if obj_type == "Person": + person = db.get_person_from_handle(obj_handle) + ... +``` + +`find_backlink_handles` returns `(class_name, handle)` tuples for every primary object that references the given handle. Use it for: + +- Finding all sources that cite a given place +- Finding all people present at a given event +- Detecting orphaned objects (no backlinks → unreferenced) + +Note that `obj_type` is the **class name as a string** (`"Person"`, `"Family"`, ...), not the Python class itself. + +## Filters + +For non-trivial selection (e.g. "all people born in Hamburg between 1850 and 1900"), use Gramps' filter framework rather than hand-rolling predicates: + +```python +from gramps.gen.filters import GenericFilterFactory + +GenericFilter = GenericFilterFactory("Person") +filt = GenericFilter() +filt.add_rule(SomeRule([arg1, arg2])) +handles = filt.apply(db, db.iter_person_handles()) +``` + +Filters compose, cache, and integrate with the GUI's filter sidebar — a report that defines its own filter gets it as a sidebar option for free. The rule catalogue lives under `gramps.gen.filters.rules`; the user-facing counterpart is documented in [Filters](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Filters). + +## Mutating data + +Write addons (mainly tools) modify data through **transactions**. The pattern is always the same: + +```python +with DbTxn(_("Tool name: what it did"), db) as trans: + person = db.get_person_from_handle(handle) + person.set_privacy(True) + db.commit_person(person, trans) +``` + +Three things matter: + +1. **The transaction message is user-visible** in the Undo History. Make it descriptive and translated. +2. **Always `db.commit_(obj, trans)`** after mutating — the object is a copy; commit writes it back. +3. **Group related changes** in one transaction so the user can undo as a single step. + +Creating a new object follows the same shape: + +```python +from gramps.gen.lib import Person, Name + +with DbTxn(_("Add unknown spouse"), db) as trans: + person = Person() + name = Name() + name.set_surname(surname) + person.set_primary_name(name) + person.gramps_id = db.find_next_person_gramps_id() + db.add_person(person, trans) +``` + +`find_next__gramps_id()` allocates an unused ID; `add_()` inserts and assigns the handle. + +## Testing data access + +Two complementary approaches: + +- **Real-data tests** — load `example.gramps` (shipped with Gramps, canonical test fixture) and exercise your code against it. Best for catching real-world data quirks (cross-typed backlinks, ID normalisation, unusual character sets). See [07-testing](07-testing.md). +- **Mocked tests** — substitute the database with a stub that returns fixed objects. Best for tight unit-test loops that don't need a database on disk. + +The lesson, learned the hard way: mocked DB tests can pass while the real-DB code is broken, because the mock doesn't reproduce the cross-typed backlinks and ID quirks of a populated tree. Prefer example.gramps for anything that traverses the DB; reserve mocks for pure helpers. + +## Performance notes + +The database API is fast enough that most addons don't need to think about performance. When you do: + +- Iterating **handles** is cheaper than iterating **objects** — only dereference when you need the object's contents. +- `get_number_of_()` is O(1); `len(list(db.iter_()))` is O(n). +- Backlinks aren't free — they read an index but still scan it. Don't call `find_backlink_handles` in a tight inner loop. +- The 5.x → 6.0 SQLite backend is roughly comparable to BSDDB for reads, faster for writes. Avoid backend-specific assumptions; addons should work on either. + +## See also + +- [04-fundamentals](04-fundamentals.md) — the plugin lifecycle that wraps this DB access in +- [06-api-reference](06-api-reference.md) — the addon-facing API surface +- [07-testing](07-testing.md) — testing strategies, real-data vs mocks +- [Using database API](https://gramps-project.org/wiki/index.php/Using_database_API) — the standalone wiki reference, covers backends and internals in more depth +- [Gramps Developer Reference](https://gramps-project.org/wiki/index.php/Gramps_6.0_Developer_Reference) — the full API docs diff --git a/docs/addon-development/06-api-reference.md b/docs/addon-development/06-api-reference.md new file mode 100644 index 000000000..fec5d9b04 --- /dev/null +++ b/docs/addon-development/06-api-reference.md @@ -0,0 +1,209 @@ +# API Reference + +[← Previous](05-data-access.md) · [Index](01-overview.md) · [Next →](07-testing.md) + + + +## Overview + +The curated `gramps.gen.*` surface addons are allowed to import. `gen` is the self-contained core submodule (it must not import from `gui` or `plugins`); importing only from `gen` keeps an addon portable across UI variants and testable without a display. + +This page is a navigator, not a generated API dump. For exhaustive signatures, read the source of the module referenced — the [upstream Sphinx docs](https://gramps-project.org/docs/) carry the same information formatted for browsing. + +## Allowed surface + +### Database + +| Module / class | Notes | +|-------------------------------------------|--------------------------------------------------------------------| +| `gramps.gen.db.base.DbReadBase` | Read-only DB interface — what addon code typically receives | +| `gramps.gen.db.base.DbWriteBase` | Mutation interface; reach via `db` after `with DbTxn(...) as trans`| +| `gramps.gen.db.txn.DbTxn` | Transaction context manager — required for every write | +| `gramps.gen.db.utils.open_database` | Open a tree by path; used in repro scripts and tests | +| `gramps.gen.db.exceptions` | DB-layer exception hierarchy | + +See [05-data-access](05-data-access.md) for the addon-facing patterns that use this surface. + +### Object model + +| Module | Notes | +|-------------------------------------|------------------------------------------------------------------------| +| `gramps.gen.lib` | Every primary class: `Person`, `Family`, `Event`, `Place`, `Source`, `Citation`, `Repository`, `Media`, `Note`, `Tag`. Plus value classes (`Name`, `Date`, `Address`, `Surname`, `EventRef`, …). | +| `gramps.gen.lib.person.Person` | The gender constants (`Person.MALE`, `Person.FEMALE`, `Person.UNKNOWN`) are class attributes | + +The full inventory is large; the cheapest reference is the source under [`gramps/gen/lib/`](https://github.com/gramps-project/gramps/tree/maintenance/gramps60/gramps/gen/lib). Every primary class has matching `get_*` / `set_*` accessors; relationships are exposed as handle lists (`get_family_handle_list`) or ref lists (`get_event_ref_list`, `get_child_ref_list`). + +### Types and IDs + +| Module | Notes | +|-----------------------|---------------------------------------------------------------------------------------------| +| `gramps.gen.types` | `PersonHandle`, `FamilyHandle`, …, `PersonGrampsID`, `FamilyGrampsID`, … | + +Prefer these over bare `str` in addon code that handles either kind of identifier. It documents intent for the next reader and makes mistakes (handle vs ID) catchable with `mypy`. See [16-guidelines → Coding style](16-guidelines.md#coding-style). + +### Errors + +| Module | Use | +|-------------------------------------|----------------------------------------------------------------------| +| `gramps.gen.errors` | Raise existing exceptions here before inventing new classes | +| `gramps.gen.errors.HandleError` | Invalid or missing handles | +| `gramps.gen.db.exceptions` | DB-layer-specific exceptions | + +### Plugin base classes + +| Class / module | Used by | +|---------------------------------------------------------|------------------------------------| +| `gramps.gen.plug.Gramplet` | `GRAMPLET` addons | +| `gramps.gen.plug.report.Report` | `REPORT` addons | +| `gramps.gen.plug.report.MenuReportOptions` | Options form for report addons | +| `gramps.gen.plug.report.stdoptions` | Pre-built options like locale chooser | +| `gramps.gen.plug.docgen.BaseDoc` | `DOCGEN` addons (base) | +| `gramps.gen.plug.docgen.TextDoc` | Text reports | +| `gramps.gen.plug.docgen.DrawDoc` | Graphical (drawing) reports | +| `gramps.gen.plug.docgen.GVDoc` | Graphviz-based reports | +| `gramps.gen.plug.docgen.FontStyle`, `ParagraphStyle` | Style definitions for text reports | +| `gramps.gen.plug.docgen.PaperStyle`, `PaperSize` | Page geometry for graphical reports | +| `gramps.gen.plug.menu` | Options-form widgets | +| `gramps.gen.filters.rules.Rule` (and namespace bases) | `RULE` addons | +| `gramps.gen.simple.SimpleAccess`, `SimpleDoc` | Quick Views | + +Most `gramps.gui.*` classes are *internal*; addons that import from there will break across Gramps versions. The exceptions used in this manual's tutorials — `gramps.gui.plug.tool.Tool`, `gramps.gui.plug.quick.QuickTable`, `gramps.gui.dialog.OkDialog` — are documented because every existing Tool / Quick View in core uses them, but they are nevertheless GUI-coupled. Pure logic factored out into modules that import only from `gen` stays unit-testable without a display. + +### Report categories + +For `REPORT` addons, register with one of these category constants (see [`_pluginreg.py:141-149`](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/plug/_pluginreg.py)): + +| Category | Docgen interface | Notes | +|------------------------|------------------------|--------------------------------------------------------| +| `CATEGORY_TEXT` | `TextDoc` | Text reports — PDF, HTML, ODF, plain text | +| `CATEGORY_DRAW` | `DrawDoc` | Graphical reports drawn at exact coordinates | +| `CATEGORY_GRAPHVIZ` | `GVDoc` | Graphviz / DOT input — laid out by graphviz | +| `CATEGORY_WEB` | (direct file I/O) | Narrative website — writes HTML/CSS directly to files | +| `CATEGORY_BOOK` | `TextDoc` + `DrawDoc` | A composition of Text and Draw reports | +| `CATEGORY_TREE` | `DrawDoc` | Genealogical tree-chart layouts | +| `CATEGORY_CODE` | (none) | Catch-all for reports that don't fit elsewhere | + +Only `CATEGORY_TEXT` and `CATEGORY_DRAW` participate in `CATEGORY_BOOK`. + +### Document API: structure at a glance + +The three docgen interfaces have distinct hierarchies. Knowing which container nests what saves a long trip through the source. + +**`TextDoc` — sequential text layout, paginated by the backend.** + +``` +Document +├── Paragraph +├── Pagebreak +├── Table +│ └── Row +│ └── Cell +│ ├── Paragraph +│ └── Image +└── Image +``` + +Paragraph styles drive titles, body text, list entries. The backend or external viewer handles pagination, except where a manual `Pagebreak` is inserted. Index marks attach to text within a paragraph (`gramps.gen.plug.docgen.IndexMark`), feeding the table of contents in Book reports. + +**`DrawDoc` — exact-coordinate graphics on a frame.** + +``` +Document +└── Frame + ├── Line + ├── Polygon + ├── Box + └── Text +``` + +The frame is the drawing surface; elements get placed by coordinates supplied by the report. The origin is the top-left of the usable area (page minus margins). Graphical reports need to honour `PaperStyle.get_usable_width()` / `…_height()` — drawing into the margins is a contract violation. + +**`GVDoc` — graphviz model.** + +``` +Document +└── Subgraph + ├── Node + ├── Link + └── Comment +``` + +The report defines nodes, links, and comments; layout is the external graphviz binary's job. This is why `requires_exe=["dot"]` appears on Graphviz-based addons. + +### Paper geometry (Draw / Tree only) + +`gramps.gen.plug.docgen.PaperStyle` holds: + +- the paper size (a `PaperSize` instance), +- margins, +- orientation (portrait / landscape). + +Convenience accessors `get_usable_width()` and `get_usable_height()` return the drawing-area dimensions (paper size minus margins, in orientation order — width is always horizontal). Text reports don't need to read these; the backend paginates around them. + +### Locale and translation + +| Module / class | Use | +|-------------------------------------------------------------|------------------------------------------------------------| +| `gramps.gen.const.GRAMPS_LOCALE` (alias `glocale`) | The live locale; entry point for `_()` injection | +| `glocale.get_addon_translator(__file__).gettext` | Bind `_` to the addon's own `po/` catalog | +| `gramps.gen.utils.grampslocale.GrampsLocale` | Instantiate directly to pin a locale in repro scripts | +| `glocale.translation.ngettext` | Plural-aware translation | +| `glocale.translation.sgettext` | Strip translator-hint prefix; used with `"hint | msg"` form| + +See [04-fundamentals → Translation](04-fundamentals.md#translation) for the addon-side opt-in, and [08-debug → Reproduction scripts that bypass the GUI](08-debug.md#reproduction-scripts-that-bypass-the-gui) for the `GrampsLocale(localedir, languages)` pattern in repros. + +### Filters and selection + +| Module / class | Use | +|---------------------------------------------------------|------------------------------------------------| +| `gramps.gen.filters.GenericFilterFactory` | Construct a filter for a namespace | +| `gramps.gen.filters.rules` | The rule catalogue (one subpackage per namespace) | +| `gramps.gen.filters.rules..Rule` | Base class to subclass when writing a custom rule (see [02-tutorials](02-tutorials.md#a-custom-filter-rule)) | + +The modern rule entry point is `apply_to_one(db, obj)` (see [`_rule.py:162`](https://github.com/gramps-project/gramps/blob/maintenance/gramps60/gramps/gen/filters/rules/_rule.py#L162)). Older code used `apply()`. + +### Logging + +| Module / class | Use | +|---------------------------|----------------------------------------------------| +| `logging.getLogger(__name__)` | Module-level logger; see [04-fundamentals → Logging](04-fundamentals.md#logging) | + +There's nothing addon-specific to import here; addons use stdlib `logging` exactly like Gramps' own modules do. + +### Simple Access (Quick Views) + +| Class | Use | +|---------------------------------------------|----------------------------------------------------------------| +| `gramps.gen.simple.SimpleAccess` | High-level DB read interface — hides handles and refs | +| `gramps.gen.simple.SimpleDoc` | Matching write interface — `title`, `paragraph`, `header1`, … | +| `gramps.gui.plug.quick.QuickTable` | Clickable result table (GUI-coupled; QuickView-only) | + +See [02-tutorials → A Quick View](02-tutorials.md#a-quick-view) for the standard pattern. + +## What's NOT API + +Anything under `gramps.gui.*` or `gramps.plugins.*` is internal to the shipped distribution; addons that import from there break across Gramps versions. The exceptions (Tool / Quick View / Dialog) are documented above and unavoidable for those addon kinds, but pure logic should be factored out behind a `gen.*`-only boundary so it stays unit-testable without a display. + +If you find yourself reaching into `gramps.gui.*` or `gramps.plugins.*` for something that *isn't* tied to GUI display, the right move is usually to ask upstream to promote what you need into `gen`. The [committing policies wiki page](https://www.gramps-project.org/wiki/index.php/Committing_policies) and the gramps-devel mailing list are the channels. + +## See also + +- [03-addon-kinds](03-addon-kinds.md) — which kinds use which base classes. +- [04-fundamentals](04-fundamentals.md) — the cross-cutting concepts (logging, translation, signals) backed by this surface. +- [05-data-access](05-data-access.md) — patterns over the DB API. +- [14-compatibility](14-compatibility.md) — what changes across Gramps versions in this surface. +- [Report API](https://gramps-project.org/wiki/index.php/Report_API), [Report Generation](https://gramps-project.org/wiki/index.php/Report_Generation) — standalone wiki references for the docgen subsystem. +- [Simple Access API](https://gramps-project.org/wiki/index.php/Simple_Access_API) — the standalone wiki page for `SimpleAccess` / `SimpleDoc`. +- [Gramps Developer Reference](https://gramps-project.org/docs/) — upstream Sphinx-generated API docs. diff --git a/docs/addon-development/07-testing.md b/docs/addon-development/07-testing.md new file mode 100644 index 000000000..907eb69ef --- /dev/null +++ b/docs/addon-development/07-testing.md @@ -0,0 +1,302 @@ +# Testing + +[← Previous](06-api-reference.md) · [Index](01-overview.md) · [Next →](08-debug.md) + + + +## Overview + +How to test an addon without launching the GUI on every iteration — the test framework, the layout conventions, the fixtures that work, and the platform-aware rules that keep tests portable across Linux, Windows, and Mac. + +A working test suite is what makes an addon **maintainable across Gramps releases**. The matrix of (Gramps version × OS) makes manual testing impossible at scale; the per-OS prefix conventions below let a single CI matrix verify your addon against every supported combination automatically. + +## Framework: stdlib `unittest` + +Use stdlib `unittest`. Don't use pytest. + +Gramps itself standardises on `unittest` (subclasses of `unittest.TestCase`), which keeps addon tests contributable upstream without a framework-conversion step. Mixing pytest features (fixtures, parametrise, plugins) breaks contribution upstream where pytest isn't installed. + +```python +import unittest + + +class MyAddonTests(unittest.TestCase): + def test_handles_empty_input(self): + # ... + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main() +``` + +### Class header convention + +The "class header navigation comment" rule from gramps' AGENTS.md is unconditional — it applies to `unittest.TestCase` subclasses too. PR 2326 round 2 caught the omission: + +```python +# ------------------------------------------------------------ +# +# MyAddonTests +# +# ------------------------------------------------------------ +class MyAddonTests(unittest.TestCase): + ... +``` + +## Layout + +Each addon ships its tests in a `tests/` subpackage: + +``` +MyAddon/ +├── MyAddon.gpr.py +├── MyAddon.py +└── tests/ + ├── __init__.py # marker — see below + └── test_myaddon.py +``` + +### Why `tests/__init__.py` exists + +The marker is **hygiene, not a bug fix**. Python 3.3+'s implicit namespace packages (PEP 420) mean a directory without `__init__.py` is still importable; dotted-path loading (`python3 -m unittest MyAddon.tests.test_myaddon`) works either way. But: + +1. **Explicit beats implicit.** "It works" is currently true by accident of invocation. The same code breaks the moment something uses `discover` or assumes regular packages. +2. **Explicit — and empty.** Suite-wide test setup (the GI version pins, warning filters) lives at the *repository* root's `tests/__init__.py`, not per addon — see the next section. The per-addon marker stays empty; it is packaging hygiene, and a home for genuinely addon-local setup only if one ever appears. + +The convention crystallises as: every addon's `tests/` **should** have an `__init__.py`; the addon directory itself **should not**. + +The asymmetry matters. The addon directory must remain a plain namespace dir — Gramps' plugin loader puts the addon dir on `sys.path` and imports `.py` by name. Making the addon dir a regular package can disturb plugin loading (and the [Mantis 12691](https://gramps-project.org/bugs/view.php?id=12691) namespace trap lives in exactly this area). The `tests/` subfolder has no such constraint, so making it an explicit package is free. + +This is what [addons-source PR 930](https://github.com/gramps-project/addons-source/pull/930) (Gary Griffin) is moving toward. + +## The GTK-pin contract + +Gramps establishes the GObject-introspection environment **once, at startup, before any plugin loads**: `gi.require_version("Gtk", "3.0")` and `gi.require_version("Gdk", "3.0")` run in `gramps/grampsapp.py` and `gramps/gen/constfunc.py`. Every addon module therefore does `from gi.repository import Gtk` into an already-pinned namespace — inside Gramps, the pin is never the addon's job. + +**The trap.** Run that module under bare `unittest` and the import warns (or resolves a different GTK) because nothing has pinned yet. The tempting fix is to copy the `gi.require_version` call into the addon module or the test file. Tests now pass — but the pin also executes inside Gramps, where it is redundant at best and a hard failure the moment the hardcoded pin and the version Gramps runs diverge: `gi.require_version` raises `ValueError` once the namespace is already loaded at a different version. That is the *works-in-tests, breaks-in-Gramps* failure mode, and it is invisible to CI because CI only runs the tests. + +**The contract**, in two halves: + +1. **Modules never pin.** No `gi.require_version` in the addon module or in any test file — the environment is provided *to* them, in both contexts. Redundant pins are safe to remove from files you are already touching (the Themes addon's `tests/__init__.py` cleanup is the precedent), but don't churn files you aren't otherwise changing. +2. **The repository root provides what Gramps provides.** addons-source carries the pins **once**, in the repo-root `tests/__init__.py` (addons-source PR [950](https://github.com/gramps-project/addons-source/pull/950)): the repo-root suite run and the CI runners import that package before any test module, pinning the whole suite to the GTK 3 / GDK 3 stack a real Gramps session uses (it also silences the locale warnings that uncompiled source-tree addons legitimately emit). The per-addon `MyAddon/tests/__init__.py` stays **empty** — see the previous section. + +The one thing a GUI-touching test module may still need is a presence guard for hosts with no PyGObject at all: + +```python +try: + import gi +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) +``` + +**The corollary: run from the repository root.** The pins execute when the root `tests` package loads — the repo-root suite run and CI's per-addon runners do that. Run your own invocations from the addons-source root too (the dotted-path form below), and never run a test file by filesystem path (`python3 MyAddon/tests/test_myaddon.py`) — the shortcut that pushes pins back into the modules, and that bypasses the namespace-package semantics the loading section below relies on. + +The GI pins are one instance of a wider rule: everything process-global that Gramps' startup owns — locale, the root logger, `sys.path`, the GTK main loop, `sys.excepthook`, environment variables — follows the same contract. The full startup surface, with the per-item temptations and alternatives, is tabulated in [04-fundamentals → The provided environment](04-fundamentals.md#the-provided-environment). + +## Filename conventions (addons-source CI) + +addons-source's CI workflow filters tests by **filename prefix** to scope them per platform: + +| Prefix | Where it runs | +|-------------------------|------------------------------------------------| +| `test_*.py` | All platforms (Linux + Windows) | +| `test_linux_*.py` | Linux only | +| `test_windows_*.py` | Windows only | +| `test_integration_*.py` | Linux only — full-pipeline / DB-backed | + +The Ubuntu runner skips `test_windows_*`; the Windows runner skips both `test_linux_*` and `test_integration_*`. Both runners include the platform-neutral `test_*.py` files. + +**Pick the prefix that matches the test's portability**, not the platform you happen to be developing on. A test that exercises POSIX file paths goes under `test_linux_*`; a test that exercises win32 locale handling goes under `test_windows_*`; everything else, the plain `test_*.py` prefix. + +CI's workflow file is authoritative: [addons-source/.github/workflows/ci.yml](https://github.com/gramps-project/addons-source/blob/maintenance/gramps60/.github/workflows/ci.yml). + +## Loading: dotted path, not `discover` + +Upstream CI loads tests by **dotted path**: + +```bash +python3 -m unittest MyAddon.tests.test_myaddon +``` + +Not by `discover` from inside an addon's `tests/` directory, and never by filesystem path. Dotted-path loading from the repo root surfaces the namespace-package trap. Bug 12691 — `from import ` binding the submodule instead of the class — only shows up under dotted-path loading. `discover`-based loading walks files by *filename*, hiding the import-resolution issue. Mirroring CI's invocation locally catches what CI catches. + +Locally, from the `addons-source` root, the same invocation works: + +```bash +# Run one test module +python3 -m unittest MyAddon.tests.test_myaddon + +# Run every test in the addon's tests/ package +python3 -m unittest discover -s MyAddon/tests -t . +``` + +The discover form here works because the addon directory is the import root — the namespace-package trap shows up only when an *individual addon module* mis-imports itself. + +## Mocked vs `example.gramps`-backed tests + +Two complementary strategies. They're not alternatives. + +### Mocked unit tests + +Fast, no DB on disk, suitable for tight branch-coverage of pure logic. Substitute the database with a stub that returns fixed objects: + +```python +import unittest +from unittest.mock import MagicMock + + +class HappyPathTests(unittest.TestCase): + def test_skips_people_without_birth(self): + person = MagicMock() + person.get_birth_ref.return_value = None + + result = pure_logic(person) + + self.assertEqual(result, expected) +``` + +The MagicMock approach has a built-in failure mode: it returns something for *every* method call, so a typo'd method name appears to work. Real DB code that fails on the next call will pass the mocked test. This is the bug the next strategy catches. + +### `example.gramps`-backed tests + +`example.gramps` ships with the Gramps source under `example/gramps/example.gramps`. It's the canonical fixture triage and developers reproduce against; loading it produces a real populated database with the cross-typed backlinks, ID normalisations, and absent optional fields that real users hit. + +```python +import os +import unittest +from gramps.gen.db.utils import open_database + + +class IntegrationTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.db = open_database( + os.path.expanduser("~/path/to/gramps/example/gramps/example.gramps") + ) + + def test_handles_real_data(self): + result = code_under_test(self.db) + self.assertGreater(len(result), 0) +``` + +Name these `test_integration_*.py` so CI scopes them to Linux only (loading a real DB is heavier, and Windows CI's Gramps setup is separately constrained — see [14-compatibility → Windows toolchain migrated to UCRT64](14-compatibility.md#windows-toolchain-migrated-to-ucrt64)). + +### Choosing between them + +| Use the mock when | Use `example.gramps` when | +|---------------------------------------------------|-----------------------------------------------------------| +| The function under test takes pure inputs | The function traverses the DB | +| You're covering many input shapes (loop / branch) | You're verifying *one* real-world scenario | +| You need sub-millisecond turnaround | You need real-data shape (backlinks, IDs, optional refs) | + +The lesson, learned the hard way: mocked tests can pass while real-DB tests fail, because the mock doesn't model what production data looks like. + +## Tests must run without `requires_mod` deps + +A hard constraint, set by Gary Griffin (2026-05-16): addon tests must run cleanly without the addon's `requires_mod` dependencies installed in the Python that runs them. Mac contributors can't easily install addon deps into the Gramps Python on macOS, and there's no Gramps debug-mode equivalent on Mac to work around it. + +Two ways to honour this: + +### Mock at the import boundary + +```python +import sys +from unittest.mock import MagicMock + +# Stand in for an optional dep before importing the addon. +sys.modules.setdefault("PIL", MagicMock()) +sys.modules.setdefault("PIL.Image", MagicMock()) + +from MyAddon.MyAddon import code_under_test +``` + +Cleaner than try/except, and the test asserts the addon's behaviour **with the dep present** — what almost every real user sees. + +### Skip cleanly + +When mocking is impractical (e.g. the dep is core to the function under test), skip without erroring: + +```python +import unittest +from importlib.util import find_spec + + +@unittest.skipUnless(find_spec("PIL"), "Pillow not installed") +class PhotoTaggingTests(unittest.TestCase): + def test_loads_jpeg(self): + ... +``` + +A failed import at module load — instead of a `skipUnless` — turns into a test error on the Mac runner, blocking the CI suite. + +## What to test + +Mandatory: + +- **The bug a fix closes.** Every bug fix ships with a test that fails pre-fix and passes post-fix. At PR level this is a [16-guidelines MUST](16-guidelines.md#contributor-workflow): the regression test, or an explicit "no test because X" rationale plus a manual repro — "add the test later" is not an option. Doc-only PRs are the only exception. + +Strongly recommended: + +- **One happy-path call** through the addon's main entry point. The smoke test that catches the next breakage. +- **One real-data scenario** against `example.gramps` for any DB-traversal code. + +Optional but valuable: + +- **Edge cases** the function explicitly handles: empty DB, missing optional fields, IDs at the boundaries of normalisation. + +What *not* to test: + +- The Gramps API itself. If `db.get_person_from_handle(h)` returns `None` for a missing handle, that's Gramps' contract; your test exercises that **your code handles `None`**, not that Gramps returns it. + +## What the test catches that the GUI doesn't + +A test surfaces failure modes the GUI cycle hides: + +- **The namespace-package trap** (bug 12691) — surfaces under dotted-path loading. +- **`requires_mod` typos** — `from import …` would fail import; surfaces immediately at test load. +- **DB-shape assumptions** — the cross-typed-backlinks / ID-norm issues that mocked tests miss. +- **Per-OS regressions** — running on both runners. + +See [09-troubleshoot](09-troubleshoot.md) for the symptoms-to-cause mapping for these classes of failure. + +## Running tests locally + +From the `addons-source` checkout root: + +```bash +# Run one addon's tests +python3 -m unittest discover -s MyAddon/tests -t . + +# Or invoke a single test module by dotted path (mirrors CI's invocation) +python3 -m unittest MyAddon.tests.test_myaddon +``` + +Run from the addons-source root, and never invoke a test file by filesystem path — see [the GTK-pin contract](#the-gtk-pin-contract). + +The Python that runs the tests needs `gramps` importable. The simplest setup is `PYTHONPATH=/path/to/gramps python3 -m unittest …`; if Gramps is installed system-wide, the import resolves without `PYTHONPATH`. + +On Windows, run from the MSYS2 UCRT64 shell against a UCRT64-installed Gramps — the AIO build for Gramps 6.1+ targets UCRT64; Gramps 6.0 isn't Windows-tested upstream. See [14-compatibility → Windows toolchain migrated to UCRT64](14-compatibility.md#windows-toolchain-migrated-to-ucrt64). + +## See also + +- [04-fundamentals → Logging](04-fundamentals.md#logging) — `LOG` setup that tests assert against. +- [05-data-access → Testing data access](05-data-access.md#testing-data-access) — DB-API patterns to exercise. +- [08-debug](08-debug.md) — turning a repro script into a test. +- [09-troubleshoot](09-troubleshoot.md) — the symptoms these tests catch in CI rather than production. +- [10-code-analysis](10-code-analysis.md) — what the static checkers verify before tests run. +- [16-guidelines → Testing](16-guidelines.md#testing) — normative rules. +- [Mantis 12691](https://gramps-project.org/bugs/view.php?id=12691) — the canonical namespace-package trap that motivates dotted-path loading. +- [addons-source PR 930](https://github.com/gramps-project/addons-source/pull/930) — `tests/__init__.py` convention. diff --git a/docs/addon-development/08-debug.md b/docs/addon-development/08-debug.md new file mode 100644 index 000000000..b12f46970 --- /dev/null +++ b/docs/addon-development/08-debug.md @@ -0,0 +1,184 @@ +# Debug + +[← Previous](07-testing.md) · [Index](01-overview.md) · [Next →](09-troubleshoot.md) + + + +## Overview + +How to see what an addon is actually doing — where it logs, how to enable verbose output, and the patterns for reproducing a problem without sitting through a full Gramps launch cycle each time. + +Most addon bugs are reachable through three escalating tools, in order: read the log window, enable per-logger debug output, or write a tight repro script that bypasses the GUI entirely. The heavier tools (pdb, gdb) are documented at the bottom for the cases where the lighter ones don't suffice. + +## Where addon output goes + +Two surfaces, both populated by the same logging calls: + +| Surface | When you see it | +|------------------------|------------------------------------------------------------------------------------------------| +| **Gramps log window** | Help → Log. Always populated. Visible to the user. | +| **stderr / terminal** | Whatever shell launched Gramps. Populated only when Gramps is run from a terminal. | + +The logging module that backs both is the stdlib `logging`; an addon's module-level logger feeds in like any other: + +```python +import logging +LOG = logging.getLogger(__name__) + +LOG.debug("Computed candidate set: %s", candidates) +LOG.info("Processed %d people", n) +LOG.warning("Skipping malformed event %s", event.gramps_id) +LOG.error("Could not parse %s", filename) +``` + +`__name__` for an addon resolves to the addon's `id` (e.g. `"MyAddon.myaddon"`), so the logger inherits the addon's name naturally — useful for per-logger filtering below. + +**Don't use `print()`.** It bypasses both surfaces and breaks under windowed launches that have no terminal attached. The [16-guidelines](16-guidelines.md#runtime) page makes this a hard rule. + +## Default log levels + +Gramps configures the root logger at `WARNING` by default; `DEBUG` and `INFO` are filtered. Two ways to lower the bar: + +### `--debug=` + +Launch Gramps with the `--debug` flag to enable `DEBUG` for one named logger: + +```bash +gramps --debug=MyAddon +gramps --debug=MyAddon.myaddon # narrower +gramps --debug=gramps.gen.db # for DB internals +``` + +Pass it more than once to enable several loggers. The flag is strictly opt-in per logger — that's why you set it on the launch command, not in code. Other loggers stay quiet, so you're not swimming in noise. + +### Module-level override (development only) + +When iterating tightly, drop a one-line override at the top of the implementation module: + +```python +import logging +logging.getLogger(__name__).setLevel(logging.DEBUG) +``` + +Remove before committing — published addons should rely on `--debug=…` so users aren't forced into verbose output. + +## Reproduction scripts that bypass the GUI + +Restarting Gramps to test a one-line change burns minutes. For anything that *can* be tested without the GUI, write a tight repro script that instantiates the addon's testable pieces directly. + +The pattern looks like this: + +```python +# repro_.py — run with `python3 repro_.py`. +import os, sys +sys.path.insert(0, os.path.expanduser("~/path/to/gramps")) + +from gramps.gen.const import GRAMPS_LOCALE +from gramps.gen.utils.grampslocale import GrampsLocale +from gramps.gen.db.utils import open_database + +# Pin the locale without touching system locale config. +glocale = GrampsLocale( + localedir=os.path.expanduser("~/path/to/gramps/po"), + languages=["fi"], # the language under test +) + +db = open_database("example.gramps") +# … exercise the buggy code path … +``` + +`GrampsLocale(localedir, languages)` is the key escape hatch — it bypasses both `LANGUAGE` env-var setup and `locale-gen`-style OS config, neither of which is needed for an in-process test. The pattern came out of triaging [Mantis 14100](https://gramps-project.org/bugs/view.php?id=14100) (Finnish month-inflection crash). + +For DB-traversal code, the canonical fixture is `example.gramps` shipped with Gramps source. Real-data tests against `example.gramps` catch bugs mocked DBs miss — see [05-data-access → Testing data access](05-data-access.md#testing-data-access). + +The same pattern, formalised as a `unittest.TestCase`, becomes a regression test. See [07-testing](07-testing.md). + +## In-app diagnostics: `PrerequisitesCheckerGramplet` + +When a user reports an addon misbehaving, the first triage step is *"what's the running environment?"* The PrerequisitesCheckerGramplet (an addon itself) lists every optional dependency Gramps detects and the version it found. Asking a reporter to install it, run it, and paste the output is the fastest baseline. + +It also surfaces missing GI bindings, which addons typically declare via `requires_gi` — a `requires_gi=[("GExiv2", "0.10")]` that fails silently is almost always something the PrerequisitesCheckerGramplet output would have surfaced. + +[Mantis 13966](https://gramps-project.org/bugs/view.php?id=13966) (active_page None on tree close) was a teardown-order bug *in* this gramplet; the fix lives in addons-source PR 913. + +## Platform notes + +**Linux** — the standard environment. `gramps --debug=…` works as documented; logging surfaces in the terminal that launched Gramps, plus the in-app log window. + +**Windows** — debug flags work the same way, but the launcher is typically a `.bat` or `.exe` shortcut rather than a terminal command. Launch from MSYS2 UCRT64 (`/ucrt64/bin/gramps`) to get a terminal attached for stderr. Some bugs reproduce only on Windows; when reporting one, include the Gramps version, the MSYS2 UCRT64 toolchain version, and the exact reproduction steps in the Mantis ticket so a Windows-equipped maintainer can confirm. + +**macOS** — there is **no Gramps debug mode equivalent on Mac**, and contributors typically can't install addon dependencies into the Gramps Python (Gary Griffin, 2026-05-16). This shapes two testing-side decisions: tests must run cleanly without `requires_mod` deps installed (see [07-testing](07-testing.md)), and a Mac repro that needs GUI inspection usually requires triaging via screenshots the reporter pastes into the Mantis ticket. + +## Heavier tools + +When the lighter approaches aren't enough. + +### `pdb` (Python debugger) + +Drop a breakpoint at the line of interest: + +```python +breakpoint() # Python 3.7+; same as `import pdb; pdb.set_trace()` +``` + +Launch Gramps from a terminal; when execution reaches the breakpoint, Gramps freezes and you get an interactive `(Pdb)` prompt. Commands: `n` (next line), `s` (step into), `c` (continue), `l` (list source), `p expr` (print). Full reference: [Python pdb docs](https://docs.python.org/3/library/pdb.html). + +### `python -m trace` + +For "where on earth does the crash come from?": + +```bash +python3 -m trace -t /path/to/Gramps.py >/tmp/trace.out +``` + +Produces every executed line of Python in the file. Huge, but `grep` of the last hundred lines often pins the crash site. + +### `gdb` (C debugger) + +For segfaults coming from C libraries (GTK, GObject Introspection): + +```bash +gdb python3 +(gdb) run /path/to/Gramps.py +# … reproduce the crash … +(gdb) bt # Python+C backtrace; the C frames pin the C-side cause +``` + +To trap GTK warnings as hard errors: + +```bash +G_DEBUG=fatal-warnings gdb python3 +``` + +Then `r /path/to/Gramps.py`; any GTK warning aborts with a backtrace showing the originating call. + +Addons rarely need `gdb` — segfaults that bubble up from C are usually GI binding issues (wrong typelib version, missing `gir1.2-*` package). When you do hit one, the C backtrace plus the user's `PrerequisitesCheckerGramplet` output typically point at the missing package. + +### Profiling + +`gramps.gen.utils.debug.profile` is a convenience wrapper around `cProfile`. Replace the call you want to profile: + +```python +from gramps.gen.utils.debug import profile + +def cb_save(self, *obj): + profile(self.save, *obj) +``` + +On the next save, a profile report goes to stdout: per-function call counts and cumulative time. Useful when a gramplet's `main()` is unexpectedly slow. + +## See also + +- [04-fundamentals → Logging](04-fundamentals.md#logging) — the conventions for setting up the logger in the first place. +- [07-testing](07-testing.md) — formalising a repro script into a regression test. +- [09-troubleshoot](09-troubleshoot.md) — symptom-first guide to the failure modes these tools surface. +- [16-guidelines](16-guidelines.md) — the rules around logging and diagnostics (logger over `print`, etc.). +- [Debugging Gramps](https://gramps-project.org/wiki/index.php/Debugging_Gramps) — the standalone wiki page; primary scraped source. +- [Logging system](https://gramps-project.org/wiki/index.php/Logging_system) — the deeper reference for Gramps' logging configuration. diff --git a/docs/addon-development/09-troubleshoot.md b/docs/addon-development/09-troubleshoot.md new file mode 100644 index 000000000..19bb7d9a0 --- /dev/null +++ b/docs/addon-development/09-troubleshoot.md @@ -0,0 +1,213 @@ +# Troubleshoot + +[← Previous](08-debug.md) · [Index](01-overview.md) · [Next →](10-code-analysis.md) + + + +## Overview + +The failure modes that bite first-time addon authors, organised by symptom. Each entry is "what you see → why → what to do." Read this sideways: jump to the symptom that matches what you're seeing, follow the link out to the relevant chapter for the fix in depth. + +For technique-level coverage (pdb, gdb, profilers), see [08-debug](08-debug.md). For the normative rules an addon must satisfy, see [16-guidelines](16-guidelines.md). + +## Loading and discovery + +### "My addon doesn't appear in any menu." + +The addon failed to register. Three usual causes, in order of likelihood: + +1. **`.gpr.py` raised at import.** Plugin discovery executes every `.gpr.py` at startup; a `SyntaxError` or import failure there silently drops the addon from the catalog. Launch from a terminal to see the traceback on stderr, or check the Gramps log window (Help → Log) for the failure entry. + +2. **`gramps_target_version` mismatch.** A `6.0` addon won't load in 6.1, and vice versa. Plugin discovery silently skips the registration entry. See [14-compatibility → `gramps_target_version` semantics](14-compatibility.md#gramps_target_version-semantics). + +3. **`id` doesn't match the folder name.** The addon's folder name and the `id` argument to `register(...)` must be identical. Gramps does not match by content — it matches by folder name and verifies against `id`. A mismatch silently drops the entry. + +The fastest check: in a Python REPL with `gramps` on `sys.path`, `exec(open("MyAddon/MyAddon.gpr.py").read())`. If it raises, you have your cause; if it returns silently and there's no entry, your `register()` call is being filtered out. + +### "My edits to the plugin file disappeared on restart." + +The user plugin directory (`~/.local/share/gramps/gramps60/plugins/…`) is the auto-sync **target**. Edits there are silently overwritten on the next save from `addons-source/`. + +**The fix.** Edit in `addons-source//` and let the sync flow do its job — see [12-packaging → Editing `addons-source/`, not the live plugin directory](12-packaging.md#editing-addons-source-not-the-live-plugin-directory). + +On Gramps 6.1+ Linux/macOS, symlinking the working tree into the user plugin directory once eliminates the copy step (commit `9443dcbb30`); on Gramps 6.0 and on Windows generally, the copy / `rsync` loop remains. + +### "The addon's folder is there but Gramps doesn't load it." + +The most common variants of the previous symptom, when ruled out: + +- **6.0 only**: the folder is reached via a symlink. Gramps 6.0 plugin discovery does **not** follow symlinks; use a physical copy or upgrade to 6.1+. (See [14-compatibility → Plugin discovery follows symlinks](14-compatibility.md#plugin-discovery-follows-symlinks).) +- **Windows, any version**: same as above — the 6.1 symlink test is skipped on Windows because the platform's symlink behaviour is inconsistent without elevated privileges. Physical copy. +- **`.gpr.py` not at top level of folder**: the registration file has to be `/.gpr.py`, not in a subfolder. + +## Imports and Python namespace traps + +### "`from import ` binds the submodule, not the class." + +The classic Gramps namespace-package trap. + +The addon folder is a *namespace package* (PEP 420), so importing `` gives you the package, not the class inside the like-named module. Code that worked under `discover`-based test loading breaks under dotted-path loading because the resolution path is different. + +**The fix.** Use the explicit submodule form: + +```python +# Wrong: binds the package (silently — until you try to use the class) +from MyAddon import MyAddon + +# Right: binds the class inside the module +from MyAddon.MyAddon import MyAddon +``` + +Mantis bug [12691](https://gramps-project.org/bugs/view.php?id=12691) is the canonical case. Upstream CI loads addon tests by dotted path rather than `discover` exactly to surface this trap; see [07-testing](07-testing.md). + +### "`requires_mod` declares `Pillow` but Gramps says it's missing." + +`requires_mod` takes the **importable** module name, not the PyPI distribution name: + +| PyPI name | Importable name | +|----------------|-----------------| +| `Pillow` | `PIL` | +| `PyYAML` | `yaml` | +| `lxml` | `lxml` | +| `python-dateutil` | `dateutil` | +| `Beautifulsoup4` | `bs4` | + +**The check.** Before pushing, verify on a system with the package installed: + +```python +from importlib.util import find_spec +assert find_spec("PIL") is not None +``` + +If `find_spec` returns `None`, the name in `requires_mod` is wrong. + +### "`requires_gi` declaration is fine on 6.0, broken on 6.1." + +GExiv2's version handling was rewritten on `maintenance/gramps61` only (addons-source PR [829](https://github.com/gramps-project/addons-source/pull/829)). An addon with `requires_gi=[("GExiv2", "0.10")]` that works on 6.0 may need a different pin on 6.1. + +**The fix.** Read the EditExifMetadata addon's GExiv2 code on the target branch before assuming a pin transfers. See [14-compatibility → GExiv2 version handling rewritten](14-compatibility.md#gexiv2-version-handling-rewritten). + +## Database access + +### "My addon iterates the DB but raises `KeyError` halfway through." + +The DB contains a reference to a handle that no longer resolves. This happens in real-world data; mocked tests don't exhibit it because mocks always return the same fixed set. + +**The fix.** Always guard handle dereferences: + +```python +event = db.get_event_from_handle(handle) +if event is None: + continue # silently skip dangling reference +``` + +See [05-data-access → Reading: one object at a time](05-data-access.md#reading-one-object-at-a-time) for the pattern. The same shape applies to every `get__from_handle` call. + +### "Backlinks return `(class_name, handle)` not `(class, handle)`." + +`db.find_backlink_handles(handle)` yields tuples whose first element is the **class name as a string** (`"Person"`, `"Family"`, …), not the Python class itself. The most common bug here is `isinstance` checks that never match. + +```python +# Wrong: +for cls, h in db.find_backlink_handles(handle): + if cls is Person: # always False — cls is "Person" + ... + +# Right: +for type_name, h in db.find_backlink_handles(handle): + if type_name == "Person": + ... +``` + +### "The fix worked in my mocked test but breaks on `example.gramps`." + +Real data has shapes the mock doesn't model: + +- **Cross-typed backlinks** — a Source can be backlinked from a Person, a Family, an Event, a Place, a Media, a Note, a Citation, and a Repository. Mocks tend to model only the type the test author was focused on. +- **ID normalisation** — `I0001` vs `I0021` vs `I12345`. A regex that matches the mock's 4-digit IDs misses the real data's variable-width IDs. +- **Optional fields actually being absent** — `person.get_birth_ref()` returns `None` in real data far more often than in mocks. + +**The fix.** Add an `example.gramps`-backed test alongside the mock. See [05-data-access → Testing data access](05-data-access.md#testing-data-access) and [07-testing](07-testing.md). + +## Translation and locale + +### "My addon translates fine on Linux, not on Windows" (or vice versa). + +The two platforms set up the locale differently: + +- **Linux** — needs `locale-gen` for the language and the `LANGUAGE` env var set (not just `LANG`). +- **Windows** — reads `LANG` directly via `win32locale.py`, no OS locale config needed. + +**The fix in repro scripts.** Sidestep both by instantiating `GrampsLocale(localedir, languages)` directly — see [08-debug → Reproduction scripts that bypass the GUI](08-debug.md#reproduction-scripts-that-bypass-the-gui). + +**The fix in production.** Make sure the per-addon `.po` files compile cleanly on both platforms (`make.py compile ` in addons-source). A `.mo` file that's missing or malformed will silently fall back to English on whichever platform fails to load it. + +### "Strings I marked with `_()` aren't translated." + +You've forgotten to bind `_` to the addon's catalog. At the top of the implementation module: + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.get_addon_translator(__file__).gettext +``` + +Without that line, `_()` falls back to Gramps' core catalog rather than the addon's own `/po/` translations. The strings stay English regardless of UI language. See [04-fundamentals → Translation](04-fundamentals.md#translation). + +## Testing + +### "Tests pass locally, fail in CI." + +Three common causes: + +1. **Filename prefix wrong for the platform.** `test_linux_*.py` is skipped on the Windows runner, `test_windows_*.py` is skipped on Linux. A test you intended as cross-platform but accidentally named with a prefix runs only where the prefix points. See [07-testing → Filename conventions](07-testing.md#filename-conventions-addons-source-ci). + +2. **`requires_mod` deps assumed in tests.** Addon tests must be runnable without the addon's `requires_mod` dependencies installed in the test Python — Mac contributors can't easily install addon deps into the Gramps Python (Gary Griffin, 2026-05-16). Mock at the import boundary or skip cleanly. + +3. **Test loaded by dotted path surfaces the namespace trap.** Local `discover` from `tests/` would hide the `from import ` bug; CI loads by dotted path (`.tests.`), which exposes it. See bug 12691. + +### "PR's pre-commit passed but CI is red." + +Pre-commit catches static checks only. Test failures (e.g. an import that breaks at module load) surface in CI's actual unit-test run, not in pre-commit. After pushing, watch the PR's checks until they finish: + +```bash +gh pr checks --watch +``` + +See [16-guidelines → Verification before commit](16-guidelines.md#verification-before-commit). + +## Pull-request shape + +### "`.gpr.py` version bump rejected on PR." + +addons-source PRs do **not** bump the addon's `version` field. The maintainer manages versions centrally. Leave the `version = "…"` line in `.gpr.py` untouched. (Tripped on addons-source PR 911.) + +### "PR sits without review." + +Two things to check: + +1. **Branch target.** `addons-source` PRs target `maintenance/gramps60`, not `master`. Gary cherry-picks forward to `gramps61`. Core PRs target `maintenance/gramps61`. A PR against the wrong branch may sit untouched waiting for retargeting. See [16-guidelines → Contributor workflow](16-guidelines.md#contributor-workflow). + +2. **PR body shape.** Reviewers expect a **`**User impact:**`** opener (before Root cause), then *Summary / What to look at / Root cause / Fix / Verification* (the #106 format). A PR body that leads with internals instead of the user-visible effect often returns without a substantive review until it conforms. + +### "PR was rejected as duplicate." + +You wrote a fix without checking that upstream already had one in flight. The pre-flight check is [16-guidelines → Contributor workflow](16-guidelines.md#contributor-workflow) — the bullets about "check upstream isn't ahead" and "if a PR already exists, VERIFY it, do not duplicate." Searching by **affected file path**, not just the bug number, is the part that catches the most duplicates. + +## See also + +- [08-debug](08-debug.md) — technique-level coverage for reproducing what these symptoms describe. +- [07-testing](07-testing.md) — the test conventions that catch many of these symptoms before they reach a user. +- [12-packaging](12-packaging.md) — the source-to-distribution flow, where the "edits disappeared" trap lives. +- [14-compatibility](14-compatibility.md) — the 6.0 vs 6.1 deltas behind several entries here. +- [16-guidelines](16-guidelines.md) — normative reference; this chapter describes what *goes wrong*, that chapter describes what *must hold*. +- [Mantis bug tracker](https://gramps-project.org/bugs) — where the recurring failures get filed. diff --git a/docs/addon-development/10-code-analysis.md b/docs/addon-development/10-code-analysis.md new file mode 100644 index 000000000..1ebb28ea4 --- /dev/null +++ b/docs/addon-development/10-code-analysis.md @@ -0,0 +1,235 @@ +# Code Analysis + +[← Previous](09-troubleshoot.md) · [Index](01-overview.md) · [Next →](11-internationalization.md) + + + +## Overview + +What automated checks run against addon code, locally and in CI, and how to keep an addon passing them. The goal is "PR opens green" — every check below catches a class of issue cheaper than a maintainer review round. + +The checks vary by repo. Two combinations matter; the cheat sheet: + +| Check | gramps core | addons-source | +|----------------------------------------|------------------------|-------------------| +| Black formatting (`--check --diff`) | pre-commit + CI | — | +| `mypy` static types | pre-commit + CI | — | +| `ruff` E9 / F63 / F7 / F82 | — | pre-commit + CI | +| `python -m py_compile` / `ast.parse` | — | pre-commit + CI | +| `msgfmt` on `po/*.po` | upstream build | upstream build | +| `pylint` ≥ 9 on new files | manual; not gated | not enforced | + +`addons-source` does **not** enforce Black today; gramps core does. This is the most common surprise for authors moving between the two. See [Black](#black) below. + +## Pre-commit + +There is no published upstream `.pre-commit-config.yaml` for `addons-source`; addon authors can install their own locally to mirror the CI gates above (ruff, py_compile), but this is convenience tooling, not an upstream requirement. The authoritative checks live in `addons-source/.github/workflows/ci.yml`; if your local pre-commit and CI disagree, CI wins. + +For gramps core, the upstream pre-commit config under the `gramps/` repo covers Black and `mypy`; install with the standard `pre-commit install` flow from the repo root. + +## Black + +[Black](https://black.readthedocs.io/) is an opinionated Python formatter. Where enforced, the CI lint job is `psf/black@stable --check --diff`; a violation fails the build and blocks merging. + +**Where enforced.** gramps core (`maintenance/gramps61` and `master`) — both pre-commit and CI. addons-source does *not* enforce Black today; PRs there go through without formatting checks. + +**The trap on gramps core.** Tiny diffs that look harmless trip the gate: + +- A mid-module-import blank line. +- A multi-line `.append()` collapsed to one line. + +PR 2326 tripped this on `cli/clidbman.py` and a new test file; the fix was a Black-cleaned force-push rebase. Run `black --check` on the changed files before pushing: + +```bash +git diff --name-only --diff-filter=ACMR origin/master...HEAD \ + | grep '\.py$' \ + | xargs --no-run-if-empty black --check --diff +``` + +## `mypy` + +Gramps core's CI runs `mypy` against the tree; type errors block the build. `*.gpr.py` plugin registration files are excluded (they run in the injected-name scope and would otherwise complain about `register`, `_`, etc.). + +This applies to gramps core only. addons-source PRs don't run `mypy`; addon Python doesn't ship type hints by default. Where an addon does add type hints, prefer the 3.10+ shape (`X | None`, `list[X]`) per [16-guidelines → Coding style](16-guidelines.md#coding-style). + +## `ruff` E9 / F63 / F7 / F82 + +addons-source's pre-commit and CI run `ruff` with a tight rule selection: + +| Code | Catches | +|-------|------------------------------------------------------------------------| +| `E9*` | Syntax errors | +| `F63` | Comparison and membership operator mistakes (`is not` vs `not is`) | +| `F7` | Imports inside dead code, syntax-level structural issues | +| `F82` | Undefined names | + +It's a syntax-and-undefined-names net, not a style enforcer — the goal is "code that *imports*", which is what gets you past the plugin-discovery gate. + +Local invocation: + +```bash +ruff check --select E9,F63,F7,F82 / +``` + +The undefined-name rule (`F82`) is the most useful single check for addon authors — `Pillow` typo'd as `Pilllow`, `gramps.gen.plugin` typo'd as `plugn`, the kind of typo that produces a silent skip in the plugin manager and no traceback. `ruff F82` catches them. + +A lint flag is a symptom, not the bug. Don't just add a `# noqa` or a defensive import to silence `F82` — read the enclosing function first. An undefined name in shipping code usually means dead or broken code. + +## `python -m py_compile` / `ast.parse` + +Both pre-commit and addons-source CI compile every changed `.py`: + +```bash +python -m py_compile /*.py +``` + +`ast.parse` is the more lenient check (won't import code, just parses); both exist because a file that compiles can still fail to import (`NameError` at module-level, missing dep). The compile pass is the absolute floor — failing it means the addon can't even register. + +## `msgfmt` on `po/*.po` + +Per-addon `.po` files have to compile to `.mo` cleanly for translations to take effect at runtime. A malformed catalog — mismatched `%s` substitutions, unclosed plural-form expression — silently falls back to English on the platform where compilation fails. Run `msgfmt -c` on every per-addon catalog before publishing. + +Local check: + +```bash +make.py gramps60 compile +``` + +`make.py compile` wraps `msgfmt` with the right paths; a failure prints the offending file and line. See [12-packaging → The localisation flow](12-packaging.md#the-localisation-flow). + +[Mantis 14234](https://gramps-project.org/bugs/view.php?id=14234) (lxml `ngettext` newline fix; addons-source PR 907) is the canonical example — a single misplaced newline in a plural form, caught by a `msgfmt -c` pass. + +## `pylint` + +Gramps' programming guidelines call for pylint ≥ 9 on new files and "changes to existing files shall not reduce the pylint score" — but this is **not** gated by CI. It's developer guidance, not a hard check. + +`pylint` doesn't run on addon code by default. When you do run it locally: + +```bash +pylint --disable=missing-docstring /.py +``` + +Run from the addon's parent directory so `pylint`'s import resolution finds it as a package. + +## Verifying `requires_mod` + +`requires_mod` takes the **importable** module name, not the PyPI distribution name (see [09-troubleshoot → `requires_mod` declares `Pillow`…](09-troubleshoot.md#requires_mod-declares-pillow-but-gramps-says-its-missing)). Before pushing, on a system with the dependency installed: + +```python +from importlib.util import find_spec +for mod in ["PIL", "lxml", "dateutil"]: + assert find_spec(mod) is not None, mod +``` + +This is a manual check, not a CI gate. It's listed here because the failure mode it catches — silent skip in the plugin manager — looks exactly like a `ruff F82` symptom but happens at a different layer. + +## Coding-standard rules worth running locally + +The full standard lives in `../gramps/AGENTS.md` and applies to all Gramps-related Python. The mechanical checks above cover formatting and syntax; the rules below need a manual pass. + +### Import grouping + +Three sections, each with a comment header: + +```python +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import os +import logging + +# ------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +# ------------------------------------------------------------------------- +from gi.repository import Gtk + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.db.base import DbReadBase +from .mymodule import MyClass +``` + +Existing code that doesn't follow this stays as-is; new code does. + +### Callback names + +Callbacks are prefixed `cb_`: + +```python +def cb_save(self, *args): + ... +``` + +`pylint` also avoids the `W0613: Unused argument` warning for `cb_*`-prefixed methods, which is convenient for GTK signal handlers that receive arguments they don't use. + +### Class headers + +Every class — including `unittest.TestCase` subclasses — carries a navigation comment header: + +```python +# ------------------------------------------------------------ +# +# MyClass +# +# ------------------------------------------------------------ +class MyClass: + ... +``` + +This is for finding the class when multiple classes share a file, not for documentation. Sphinx-style docstrings handle the documentation. + +### Member-name conventions + +- `__private` (two underscores) — class-only access. +- `_protected` (one underscore) — class and subclass access. + +PEP 8 with one local addition: a space after every comma. + +### TAB stops + +No TABs in Python. Indentation is 4 spaces. Where TABs are unavoidable (Makefiles), they're at columns 9, 17, 25, … (equivalent to 8 spaces). Don't set your editor's TAB stops to 4 — that "fixes" indentation by making TABs invisible and produces files that look right but parse wrong. + +## Running everything locally before pushing + +A pragmatic checklist before opening a PR: + +```bash +# Addons-source PRs: +ruff check --select E9,F63,F7,F82 / +python -m py_compile /*.py +make.py gramps60 compile # exercises msgfmt +python -m unittest discover -s /tests -t . # tests + +# Gramps core PRs add: +black --check --diff .py +mypy +GRAMPS_RESOURCES=. python3 -m unittest discover -p "*_test.py" +``` + +See [12-packaging](12-packaging.md) for `make.py` setup and [07-testing](07-testing.md) for the test-loading conventions. + +## See also + +- [04-fundamentals](04-fundamentals.md) — the conventions the static checks verify. +- [07-testing](07-testing.md) — the runtime checks that complement static analysis. +- [09-troubleshoot → "PR's pre-commit passed but CI is red"](09-troubleshoot.md#prs-pre-commit-passed-but-ci-is-red) — the most common code-analysis-related symptom. +- [12-packaging](12-packaging.md) — `make.py` invocations. +- [16-guidelines → Coding style](16-guidelines.md#coding-style), [16-guidelines → Verification before commit](16-guidelines.md#verification-before-commit) — normative rules. +- [Programming guidelines](https://gramps-project.org/wiki/index.php/Programming_guidelines) — the standalone wiki page; primary scraped source. +- `../gramps/AGENTS.md` — the full Python coding standard, inherited from gramps core. diff --git a/docs/addon-development/11-internationalization.md b/docs/addon-development/11-internationalization.md new file mode 100644 index 000000000..5405ae82e --- /dev/null +++ b/docs/addon-development/11-internationalization.md @@ -0,0 +1,180 @@ +# Internationalization + +[← Previous](10-code-analysis.md) · [Index](01-overview.md) · [Next →](12-packaging.md) + +## Overview + +Gramps is a highly globalized application, and addons should be fully translatable to support users worldwide. This guide covers how to prepare your addon for internationalization (i18n), manage translation strings using `gettext`, and package translations with your addon. + +See [the addon development overview](01-overview.md) for where this fits into the broader addon lifecycle. + +## Working example + +To make strings in your addon translatable, you need to mark them using the standard translation functions, extract them into a template (`.pot`), and provide translations (`.po`). + +### Registration + +In your `*.gpr.py` file, the strings you provide for `name`, `description`, etc., should use `_()` so they can be extracted by Gramps' build tools. + +You do not need to import `_` in the `.gpr.py` file — Gramps' plugin registration loader pre-defines it to use your locale translations. Just mark strings with `_("TEXT")` and supply a translation in your `.po` file. + +```python +# exampleaddon.gpr.py +register( + KIND, + id="ExampleAddon", + name=_("Example Addon"), + description=_("A sample addon to demonstrate internationalization."), + version="1.0.0", + gramps_target_version="6.0", + status=STABLE, + fname="exampleaddon.py", +) +``` + +### Implementation + +Inside your Python implementation, you must set up your addon's translation domain or use the core Gramps translation tools if contributing to the main repository. + +```python +# exampleaddon.py +import os +from gramps.gen.plug import Gramplet + +# Typical setup for an external addon to manage its own translation domain +from gramps.gen.const import GRAMPS_LOCALE as glocale +_ = glocale.get_addon_translator(__file__).gettext + +class ExampleAddon(Gramplet): + def init(self): + # A simple translated string + message = _("Welcome to the Example Addon!") + self.set_text(message) + + def show_items(self, count): + # Using ngettext for proper pluralization + ngettext = glocale.get_addon_translator(__file__).ngettext + msg = ngettext( + "Found %d item.", + "Found %d items.", + count + ) % count + print(msg) +``` + +## Translating UI Files (Glade) + +Gramps' addon translation tools only automatically extract and manage Python strings. If your addon uses a Glade (`.ui` / `.glade`) file for its interface, those strings will not be picked up by the standard addon translation workflow. The recommended pattern is to mark the Glade strings as translatable (so they show up for translators), then override the label at runtime from Python so they get translated through your addon's gettext domain. + +1. Give the relevant widget a meaningful `id` in the `.glade` file (not the autogenerated `label3`-style id), so your Python code can look it up: + + ```xml + + place|Name: + + ``` + + The `place|` prefix is a translator context hint (see [String Marking Rules](#string-marking-rules)) — it tells the translator which sense of "Name" you mean, and is stripped before display. + +2. In the corresponding dialog's `__init__`, override the label with the runtime-translated string: + + ```python + PLACE_NAME = _("place|Name:") + + # inside __init__: + self.get_widget("place_name_label").set_label(PLACE_NAME) + ``` + + The exact setter depends on the widget — `GtkLabel` uses `set_text`, `GtkButton` uses `set_label`, etc. + +3. Re-run `make.py … init` so the new string lands in `template.pot`, then translate and test. + +## String Marking Rules + +| Function | Meaning / Usage | +|----------|---------| +| `_("...")` | Standard string translation. Marks a string for extraction and translates it at runtime. | +| `N_("...")` | Marks a string for extraction but *does not* translate it at runtime. Useful for defining lists of strings that will be translated later when displayed. | +| `ngettext("Singular", "Plural", n)` | Translates a string while applying the correct pluralization rules for the target language based on the integer `n`. | +| `_("Context\|String")` | The Gramps convention for translator context. Prefix the user-facing string with a short hint plus a pipe (`\|`). The translator sees the hint in the `.po` file and renders only the post-pipe portion. The same `_` handles it — no special function call is needed. The two-arg form `_("String", "Context")` works equivalently and dispatches to `pgettext` under the hood. | + +**Canonical context example:** the English word "Title" can mean the title of a *book* or the nobility *title* of a person. In many languages these need different translations. Mark them as: + +```python +_("book|Title") +_("person|Title") +``` + +Translators see the hint, drop the `prefix|` part, and translate the two senses independently. This is the form used throughout `addons-source` today; don't reach for `pgettext` or `sgettext` directly — go through `_`. + +**Note on obsolete functions:** In older versions of Gramps (pre-Gramps 4), you may have seen `lgettext`, `ugettext`, `lngettext`, and friends. The `l*` variants returned strings encoded according to the current locale (bytes, not text), and the `u*` variants existed only to force Unicode output under Python 2. With Python 3, all strings are Unicode by default, so both families became redundant. Use `_` (i.e. `gettext`) and `ngettext` — they always return translated strings as Python `str`. `sgettext` and `pgettext` also exist as internal helpers, but addon code should go through `_`. + +## Weblate (Gramps 6.0+) + +> **Gramps 6.0 Weblate Workflow:** Starting with Gramps 6.0 (and *only* 6.0), addon translations can be done collaboratively on the Gramps Weblate platform. The `Third-party Addons` component contains aggregated translations for every addon. If your addon is hosted in the official repository, you do not need to manually manage `.po` files. + +## Managing Translations Manually with `make.py` + +If you are managing translations manually (or for older Gramps versions), the Gramps `addons-source` repository provides a `make.py` script to manage the entire lifecycle of your translations. This script relies on the standard `gettext` tools. + +Assuming you are in the `addons-source` directory and your addon is named `ExampleAddon`, here is the workflow: + +### 1. Extracting Strings (Template Generation) + +To extract all marked strings from your Python files and generate the `template.pot` file: +```bash +python3 make.py gramps60 init ExampleAddon +``` +This command parses your addon, creates necessary subdirectories (like `po/`), and writes the base `.pot` template. + +### 2. Adding a New Language + +To initialize a translation file for a specific locale (e.g., French `fr`): +```bash +python3 make.py gramps60 init ExampleAddon fr +``` +This creates a new, empty `po/fr-local.po` file based on your template. A translator can now open this `.po` file in a tool like Poedit to provide translations. + +### 3. Updating Translations + +If you modify your Python code and add new strings, you must update your templates and existing language files: +```bash +python3 make.py gramps60 update ExampleAddon fr +``` +This synchronizes the existing `.po` file with the latest `template.pot` without destroying existing translations. + +### 4. Compiling Translations + +When testing locally or preparing to package, compile the human-readable `.po` files into binary `.mo` files (which are placed in `locale//LC_MESSAGES/.mo`): +```bash +python3 make.py gramps60 compile ExampleAddon +``` +*Note: To compile all projects in your local repository at once, use `compile all` instead of `ExampleAddon`.* + +Before committing a hand-edited `.po`, run a quick syntax sanity check: +```bash +msgfmt -c po/fr-local.po +``` +This catches malformed headers, missing/mismatched format placeholders, and broken plural forms without going through the full `make.py` pipeline. + +### 5. Building for Release + +When your addon is ready, the build command will package everything, including the compiled translations, into a `.tgz` archive: +```bash +python3 make.py gramps60 build ExampleAddon +``` + +## Implementation notes + +- **Do not use f-strings or `.format()` inside the translation wrapper:** Translation tools like `xgettext` cannot extract dynamically generated strings. You must use old-style `%` formatting or translate the static template string first before calling `.format()`. + - **Bad:** `_(f"User {name}")` + - **Good:** `_("User %s") % name` +- **Context is key:** If a word can mean multiple things (e.g., "Date" as a fruit vs. "Date" as a calendar day), consider adding translation context comments so translators know how to interpret it. +- **Extraction:** Addons distributed in the `gramps-addons` repository have their translation strings automatically extracted into a `.pot` file by the Gramps translation infrastructure. + +## See also + +- [Addon Development overview](01-overview.md) +- [Coding for translation](https://gramps-project.org/wiki/index.php/Coding_for_translation) — the core-side counterpart to this page; covers conventions for marking strings in Gramps itself. +- [Translating Gramps](https://gramps-project.org/wiki/index.php/Translating_Gramps) — general guidelines for translators (`.po` headers, plural forms, context, mnemonics). +- [Python `gettext` documentation](https://docs.python.org/3/library/gettext.html) — primary reference for `gettext`, `ngettext`, and the `GNUTranslations` class that backs them. diff --git a/docs/addon-development/12-packaging.md b/docs/addon-development/12-packaging.md new file mode 100644 index 000000000..4dac98850 --- /dev/null +++ b/docs/addon-development/12-packaging.md @@ -0,0 +1,285 @@ +# Packaging + +[← Previous](11-internationalization.md) · [Index](01-overview.md) · [Next →](13-community.md) + + + +## Overview + +From "works on my machine" to "users can install it from the addon manager." This chapter is the source-to-distribution pipeline: how `addons-source` becomes a `.addon.tgz` in `addons`, how the in-app addon manager picks it up, and what to send upstream. + +The normative *rules* a submission must satisfy (branch targeting, version-field discipline, PR body shape, Mantis trailers) live in [16-guidelines](16-guidelines.md). This page covers the *workflow* — what to run, what files appear, where they end up. + +## The three repositories + +![Fig. 1 — The source-to-distribution pipeline. Authors edit in `addons-source/`; `make.py build` packages each addon into `addons/grampsXY/download/.addon.tgz` and `make.py listing` refreshes `addons/grampsXY/listings/*.json`; the in-app addon manager fetches both over HTTPS and installs to the user's plugin directory. Edits in the user plugin dir are not pushed back — the flow is one-way only.](_media/packaging-pipeline.svg) + +Gramps addons live across three repositories. You'll have all three cloned side-by-side under one base directory: + +``` +base/ +├── gramps/ # gramps-project/gramps — Gramps itself; source of truth for the API +├── addons-source/ # gramps-project/addons-source — addon source code, one folder per addon +└── addons/ # gramps-project/addons — built distribution, one folder per Gramps version +``` + +| Repo | What's there | You edit? | +|-----------------|-------------------------------------------------------------------------------------|------------------------------------| +| `gramps` | The Gramps source tree; provides `GRAMPSPATH` for the build | No (unless writing a core change) | +| `addons-source` | The addon source: `/.gpr.py`, `/.py`, `po/`, `tests/` | **Yes — author addons here** | +| `addons` | Built `.addon.tgz` packages and listing JSON, organised by Gramps minor | No — `make.py` writes to it | + +`addons` is the **output**. The in-app addon manager hits its HTTPS mirror to fetch listings and downloads. Editing files in `addons` directly does nothing — the next `make.py` run overwrites them. + +### Branch directory split inside `addons/` + +Inside `addons/`, each Gramps minor gets its own subdirectory: + +``` +addons/ +├── gramps42/ +├── gramps50/ +├── gramps51/ +├── gramps52/ +├── gramps60/ +│ ├── download/ # .addon.tgz files +│ └── listings/ # JSON catalogues fetched by the addon manager +└── gramps61/ + ├── download/ + └── listings/ +``` + +The same addon can ship to multiple minors, each with its own `.addon.tgz` — that's why the addon manager reads only the listing for the running Gramps version. + +## Initial clone + +```bash +mkdir gramps-addons && cd gramps-addons + +git clone https://github.com/gramps-project/gramps.git +git clone https://github.com/gramps-project/addons-source.git +git clone https://github.com/gramps-project/addons.git + +cd addons-source +git checkout -b gramps60 origin/maintenance/gramps60 # for 6.0 +# or for master/6.1: +# git checkout -b gramps61 origin/master +``` + +The branch you check out in `addons-source` determines which Gramps minor your built addons target — `make.py` reads the branch name to pick the output directory inside `addons/`. + +See [14-compatibility](14-compatibility.md) for branch-targeting guidance per Gramps minor. + +## Build prerequisites + +`make.py` calls out to two environment things and one OS tool: + +- **`GRAMPSPATH`** — absolute path to your `gramps/` clone. +- **`LANGUAGE`** — must be set to `en_US.UTF-8` for the build to run. +- **`intltool`** — `sudo apt-get install intltool` on Debian/Ubuntu. + +The standard invocation: + +```bash +GRAMPSPATH=/path/to/gramps LANGUAGE='en_US.UTF-8' python3 make.py gramps60 +``` + +Cumbersome to type each time. Set the env vars in your shell startup once; only the `make.py` line varies per command. + +In the examples below, `gramps60` is the maintenance/gramps60 target; substitute `gramps61` when you're working on the master branch. + +## `make.py` cheat sheet + +`make.py` lives at the top of `addons-source` and runs against one addon at a time, or `all` for everything. The commands you'll use most: + +| Command | What it does | +|------------------------------------------|-------------------------------------------------------------------------------| +| `make.py gramps60 init ` | Create `/po/template.pot` from extracted strings | +| `make.py gramps60 init ` | Create `/po/-local.po` from the template | +| `make.py gramps60 update ` | Merge new strings from Gramps + the addon into `-local.po` | +| `make.py gramps60 compile ` | Compile every `-local.po` into `.mo` files | +| `make.py gramps60 build ` | Compile translations *and* produce `.addon.tgz` in `addons/gramps60/download/` | +| `make.py gramps60 listing ` | Refresh `addons/gramps60/listings/*.json` so the addon manager sees it | +| `make.py gramps60 clean ` | Delete generated files (`locale/`, `*.mo`) — run before `git add` | +| `make.py gramps60 build all` | Build every addon | + +`build` includes `compile`, so the standard release cycle is `clean` → edit → `build` → `listing` → commit + push to `addons/`. + +### What `build` packages + +By default `build` includes: + +- every `*.py` in the addon folder, +- every `*.glade`, `*.xml`, `*.txt`, +- every `locale/*/LC_MESSAGES/*.mo`. + +Anything else — README images, extra data files, help HTML — needs an explicit `MANIFEST` file in the addon's root listing them, **with the addon folder name prefixed on each line**: + +``` +/README.md +/help/index.html +/data/* +``` + +The `MANIFEST` mechanism was added in Gramps 5.0 and is the way to ship anything beyond the default file types. + +## The localisation flow + +Per-addon translations live under `/po/`. They are independent of Gramps' core catalogues — Gramps' plugin loader binds `_()` to the addon's own catalog when the implementation module declares: + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale + +_ = glocale.get_addon_translator(__file__).gettext +``` + +See [04-fundamentals → Translation](04-fundamentals.md#translation) for the full opt-in. Without that line, strings fall back to the core catalog regardless of where translators put them. + +### Adding a new language + +```bash +# 1. Generate (or refresh) the template from extracted strings. +make.py gramps60 init + +# 2. Initialise a fresh language file from the template. +make.py gramps60 init fr + +# 3. Translator edits /po/fr-local.po manually. + +# 4. Recompile so the .mo file lands under /locale/fr/LC_MESSAGES/. +make.py gramps60 compile + +# 5. Commit the .po (NOT the .mo or the generated locale/ tree). +git add /po/fr-local.po +git commit -m "Add French translation for " +``` + +### Refreshing an existing language + +When you've changed user-visible strings in the addon, every existing `-local.po` needs new entries merged in: + +```bash +make.py gramps60 update fr +``` + +This preserves existing translations and marks new or changed entries as `fuzzy` for the translator to review. + +### Editing `template.pot`'s header once + +The header of `/po/template.pot` carries author, maintainer, and team metadata. Edit it after the first `init` — the values propagate into every per-language file the next time you `init `. + +### What to commit, what to skip + +Commit: + +- `/po/template.pot` +- `/po/-local.po` (one per language) + +Don't commit: + +- `/locale/**` — generated by `compile` and `build`. Always run `make.py gramps60 clean ` (or `rm -rf /locale`) before `git add`. +- `/*.mo` outside `locale/` — same reason. + +## Publishing to `addons/` + +`build` writes one `.addon.tgz` per addon; `listing` rebuilds the JSON catalogues the addon manager fetches. Both go into the `addons/` repository and need committing **there**, not in `addons-source/`: + +```bash +# In addons-source/, build the package. +make.py gramps60 build +make.py gramps60 listing + +# Switch to the addons/ checkout. +cd ../addons + +# Stage the new .tgz and the refreshed listings. +git add gramps60/download/.addon.tgz +git add gramps60/listings/* +git commit -m "Add for Gramps 6.0" + +# Push when you have write access — see [16-guidelines] for the review gate. +``` + +The in-app addon manager hits the `addons` repo over HTTPS and shows the addon to users on their next "Check for updates" cycle. + +## Editing `addons-source/`, not the live plugin directory + +During development it's tempting to edit directly in `~/.local/share/gramps/gramps60/plugins//` so a Gramps restart picks up the change without copying. **Don't** — that directory is the auto-sync *target*, and Gramps writes back through it on every save. Edits made there are silently overwritten on the next source save. + +Edit in `addons-source//`. The dev loop is one of: + +- **Gramps 6.0** — copy / `rsync` the folder into the user plugin directory on each save. Plugin discovery doesn't follow symlinks. +- **Gramps 6.1+ on Linux/macOS** — symlink the working tree into the user plugin directory once, then edit in place. Plugin discovery follows symlinks with realpath-based loop dedup (commit `9443dcbb30`). +- **Windows, any version** — physical copy. The 6.1 symlink test is skipped on Windows because the platform's symlink behaviour is inconsistent without elevated privileges. + +See [01-overview → Where addons live](01-overview.md#where-addons-live) for the user plugin directory paths. + +## Submitting a new addon + +The first time you add an addon to `addons-source/`: + +```bash +cd addons-source + +# 1. Create the folder and the two required files. +mkdir +$EDITOR /.gpr.py +$EDITOR /.py + +# 2. (Optional, recommended) Translation scaffold + tests. +mkdir /po /tests +$EDITOR /tests/__init__.py # empty marker — see 07-testing +$EDITOR /tests/test_.py + +# 3. Clean any build artefacts before committing. +make.py gramps60 clean + +# 4. Commit and push to your fork, then open a PR. +git add +git commit -m "Add : " +``` + +Open the PR against the **correct branch** — for an addon targeting Gramps 6.0, that's `maintenance/gramps60`, which the maintainer cherry-picks forward to `gramps61` (see [16-guidelines → Contributor workflow](16-guidelines.md#contributor-workflow) for the full submission shape). + +## Update an existing addon + +After editing source: + +```bash +cd addons-source + +# 1. Iterate: edit, restart Gramps, verify behaviour. +# 2. Refresh translations if user-visible strings changed. +make.py gramps60 update all +# 3. Compile-check. +make.py gramps60 compile +# 4. Clean and commit. +make.py gramps60 clean +git add +git commit -m ": " +``` + +The `version` field in `.gpr.py` **stays untouched** in PRs — the maintainer manages versions centrally. (See [16-guidelines](16-guidelines.md#contributor-workflow); this came up on PR 911 where a bump was rejected.) + +## See also + +- [01-overview](01-overview.md) — what an addon is. +- [04-fundamentals → Translation](04-fundamentals.md#translation) — the `_()` injection that makes per-addon `.po` files load. +- [07-testing](07-testing.md) — what to put in `tests/` before packaging. +- [14-compatibility](14-compatibility.md) — picking the right Gramps minor and branch for your addon. +- [16-guidelines](16-guidelines.md) — the normative rules a PR must satisfy. +- [Addons development](https://gramps-project.org/wiki/index.php/Addons_development) — the standalone wiki page; primary scraped source for this chapter. +- [addons-source CONTRIBUTING.md](https://github.com/gramps-project/addons-source/blob/maintenance/gramps60/CONTRIBUTING.md) — the addons-source-side contributor guide. diff --git a/docs/addon-development/13-community.md b/docs/addon-development/13-community.md new file mode 100644 index 000000000..9478c8265 --- /dev/null +++ b/docs/addon-development/13-community.md @@ -0,0 +1,84 @@ +# Community + +[← Previous](12-packaging.md) · [Index](01-overview.md) · [Next →](14-compatibility.md) + + + +## Overview + +When the PR is merged and the package published ([Packaging](12-packaging.md)), the addon *exists* — but nobody can find it, read about it, or reach you about it. This page covers the four steps that make a merged addon part of the ecosystem: the addon-list entry, the addon's own wiki page, the announcement, and the ongoing support duty. None of them touch code; all of them decide whether the addon gets used. + +## List your addon + +Add a row for your addon to the release's addon list — [6.0 Addons](https://gramps-project.org/wiki/index.php/6.0_Addons) for the current release, or the next release's list (e.g. [6.1 Addons](https://gramps-project.org/wiki/index.php/6.1_Addons)) if the addon targets an unreleased minor. Copy an existing row and fill in the columns; the [Addon list legend](https://gramps-project.org/wiki/index.php/Addon_list_legend) explains what each column means (type, audience, rating, contact, download). + +The row skeleton, as it appears in the list page's wiki source: + +``` +|- +| +| +| +| +| +| +| +| +|- +``` + +This listing is what users browse; the Plugin Manager's download listing ([Packaging](12-packaging.md) → the `make.py listing` step) is what Gramps itself reads. An addon needs both. + +## Document your addon + +Give the addon its own wiki support page — the page the addon list's first column links to. Examine other addons' pages for the format; the conventional skeleton: + +``` +{{Third-party plugin}} + +== Usage == + +=== Configure Options === + +== Features == + +== Prerequisites == + +== Issues == + +[[Category:Addons]] +[[Category:Plugins]] +[[Category:Developers/General]] +``` + +Only add the sections the addon needs — a Gramplet with no options doesn't need *Configure Options*. The `{{Third-party plugin}}` template expands to the standard notice that the addon is third-party and where to report problems; every addon page carries it. + +## Announce the addon + +Join the [Gramps forum](https://gramps.discourse.group/) and announce the addon to users: what it does, why you built it, and how to use it. This is the step authors skip most — and an unannounced addon is invisible to the users who would have wanted it. + +## Support it through the issue tracker + +Register on the [Gramps MantisBT tracker](https://gramps-project.org/bugs/) and check it regularly. **There is no automated notification** that routes issues against your addon to you — reports sit unseen unless you look. (For fix workflow, the tracker conventions, and the commit-message trailers that close Mantis issues, see [Rules](16-guidelines.md) → Commit messages.) + +Users don't read code and they make assumptions; reports will be ambiguous or wrong about the cause. Be kind and guiding — a curt reply from an addon's own author is the fastest way to lose the users the announcement won. + +## Why addons exist + +Worth keeping in mind across the maintenance years that follow ([Compatibility](14-compatibility.md), [What's New](15-whats-new.md)): the addon channel is deliberately low-barrier. It provides: + +- a quick way for anyone to share their work — the project has never refused an addon; +- a place for a component to evolve continuously, often before core acceptance; +- a home for plugins that will never be accepted into core but are loved by many users; +- a place for experimental components to live. + +## See also + +- [Packaging](12-packaging.md) — the build/listing mechanics that precede these steps. +- [Compatibility](14-compatibility.md) — keeping the published addon working across Gramps versions. +- [Addons development](https://gramps-project.org/wiki/index.php/Addons_development) — the upstream page these steps derive from. +- [6.0 Addons](https://gramps-project.org/wiki/index.php/6.0_Addons) — the addon list itself. diff --git a/docs/addon-development/14-compatibility.md b/docs/addon-development/14-compatibility.md new file mode 100644 index 000000000..1d5a413fe --- /dev/null +++ b/docs/addon-development/14-compatibility.md @@ -0,0 +1,114 @@ +# Compatibility + +[← Previous](13-community.md) · [Index](01-overview.md) · [Next →](15-whats-new.md) + + + +## Overview + +How an addon survives — or fails — across Gramps versions. Two things to understand: the `gramps_target_version` contract (Gramps' minor matters; majors aren't even discussed), and the concrete deltas between adjacent maintenance branches that bite ports in practice. + +A working addon for Gramps 6.0 is usually a working addon for 6.1 with **zero** code changes. The exceptions are documented here; when in doubt, the safest move is to maintain one addon folder per Gramps minor in parallel `maintenance/gramps*` branches of `addons-source`. + +## `gramps_target_version` semantics + +The `.gpr.py` registration declares which Gramps minor the addon targets: + +```python +register( + GRAMPLET, + id="MyAddon", + gramps_target_version="6.0", # major.minor + ... +) +``` + +Gramps matches this **on the major.minor pair** at plugin discovery. A `6.0` addon will not load in 6.1, and a `6.1` addon will not load in 6.0 — the plugin manager silently skips the registration entry. + +### Supporting multiple minors + +The one-addon-per-minor convention is enforced by the branch directory split in the [`addons/`](https://github.com/gramps-project/addons) repo and the matching `maintenance/gramps*` branches in [`addons-source/`](https://github.com/gramps-project/addons-source). For an addon that supports 6.0 and 6.1: + +``` +addons-source @ maintenance/gramps60: MyAddon/MyAddon.gpr.py declares "6.0" +addons-source @ maintenance/gramps61: MyAddon/MyAddon.gpr.py declares "6.1" +``` + +A single `make.py gramps60 build MyAddon` on `maintenance/gramps60` produces the 6.0-targeted `.addon.tgz`; the same command with `gramps61` on `maintenance/gramps61` produces the 6.1-targeted one. See [12-packaging](12-packaging.md) for the workflow. + +When the **code is identical** between minors, the maintainer forward-merges the `maintenance/gramps60` branch into `maintenance/gramps61` and rebuilds — no per-minor source maintenance needed. + +When the code **isn't identical** (e.g. the GExiv2 version handling delta below), the two branches diverge intentionally, and you commit the minor-specific fix to each. + +## Branch targeting for fixes + +The rule that determines which branch a fix lands on differs between the two repos: + +- **`addons-source/`** → `maintenance/gramps60`. Gary cherry-picks forward to `gramps61`. (Gary Griffin, addons-source PR 915, 2026-05-24.) +- **`gramps/`** (core) → `maintenance/gramps61`. Fixes and cleanups go on the current production branch and forward-merge to `master`. Only genuinely new-feature work targets `master`. (jralls, gramps#2298.) + +A reviewer's instruction on a specific PR overrides the default (e.g. Nick-Hall asking for `master` on gramps#2299). See [16-guidelines → Contributor workflow](16-guidelines.md#contributor-workflow) for the normative form. + +## "Applies cleanly" is not "remains correct" + +A cherry-pick that `git` accepts without conflict can still be wrong on the target branch — the branches' *related* code may have changed even though the patch's hunks didn't. + +**Concrete example.** addons-source PR 829 rewrote GExiv2 version handling on `maintenance/gramps61` only. An addon that pins `requires_gi=[("GExiv2", "0.10")]` is fine on 6.0; the same pin on 6.1 may need adjustment because the code that reads the pin has changed shape. A cherry-pick of the addon would land cleanly and still be wrong. + +**The check.** Before treating a cross-branch port as done, diff the related code on the target branch — not just the file the patch touched. Read the surrounding functions; read the modules the declaration interacts with. + +## Notable 6.0 → 6.1 deltas + +The complete delta lives in the Gramps changelog; the entries below are the ones that have repeatedly affected addon authors. + +### Plugin discovery follows symlinks + +Gramps 6.0 plugin discovery **does not** follow symlinks; the addon folder must be physically present under the plugin path. Gramps 6.1 follows symlinks with realpath-based dedup against symlink loops (commit [`9443dcbb30`](https://github.com/gramps-project/gramps/commit/9443dcbb30) on `maintenance/gramps61`, with `_manager_symlinks_test.py` covering both the scan-via-symlink and loop-terminates cases). + +**Impact on dev loop.** On 6.0, copy or `rsync` the working tree into the user plugin directory on every save. On 6.1+ Linux/macOS, symlink once and edit in place. On Windows, the symlink test is skipped because the platform's symlink behaviour is inconsistent without elevated privileges; physical copy remains the safe approach on 6.1+ too. + +### Windows toolchain migrated to UCRT64 + +Gramps' Windows build migrated from MINGW64 to MSYS2 **UCRT64** in gramps PR [#2198](https://github.com/gramps-project/gramps/pull/2198) on `maintenance/gramps61` (merged 2026-04-19). MINGW64's Python target triple is rejected by orjson's `maturin` backend, so the change was forced. + +**Impact on addon Windows testing.** Addon tests run on UCRT64 on 6.1 and master only. Windows testing on 6.0 is unsupported by upstream's addons-source CI. See [07-testing](07-testing.md) for the filename-prefix convention that selects per-OS tests. + +### GExiv2 version handling rewritten + +addons-source PR [829](https://github.com/gramps-project/addons-source/pull/829) rewrote the GExiv2 version handling on `maintenance/gramps61`, in the EditExifMetadata addon. An addon that interacts with GExiv2 via `requires_gi` may need branch-specific declarations. + +**The check.** When the addon imports `GExiv2` or declares it in `requires_gi`, read the EditExifMetadata addon on the *target* branch before assuming a pin is correct. + +### BSDDB-on-Windows skip + +A test-skip rule for BSDDB on Windows landed on `maintenance/gramps61` only. Addons that exercise the BSDDB backend in tests need to account for the absence of BSDDB on Windows 6.1, not assume the 6.0 behaviour transfers. + +## Reading the deprecation signal + +When core deprecates an API, addon authors see two things in order: + +- A `DeprecationWarning` raised the first time the deprecated symbol is touched. Visible when Gramps runs with `python -W default`, or in the Gramps log window at `WARNING` level. +- A scheduled removal in the next major release. + +**Practical step.** Once a release, launch with `python -W default` against `example.gramps` and skim the log window. Every `DeprecationWarning` is a maintenance task for the next minor; deferring them until removal turns "the addon shows up but does nothing" bugs into the dominant porting failure mode. + +For the actual deprecated surface in the running Gramps, the authoritative reference is the source — search `gramps/gen/**/*.py` for `DeprecationWarning` on the target branch. + +## Sanity checks before a port + +1. **Read the new branch's relevant code.** Not the patch — the surrounding code. The patch lands; the assumption around it may have shifted. +2. **Run the addon's tests on the new branch.** The whole point of the per-OS prefix convention in [07-testing](07-testing.md) is to catch this exact case. +3. **Reproduce against `example.gramps` on both branches.** The canonical fixture is identical across minors, so an output difference is an actionable signal. +4. **Check the open PRs against `gramps` and `addons-source` for anything affecting your addon.** A fix may be in flight upstream; verifying that PR is usually better than writing your own. + +## See also + +- [01-overview → Where addons live](01-overview.md#where-addons-live) — the 6.0 vs 6.1 symlink discovery rule, with the dev-loop consequence. +- [04-fundamentals → The `.gpr.py` registration file](04-fundamentals.md#the-gprpy-registration-file) — `gramps_target_version` declaration in context. +- [12-packaging](12-packaging.md) — how the per-minor build flow uses `gramps_target_version`. +- [15-whats-new](15-whats-new.md) — scheduled per-release changes affecting addon authors. +- [16-guidelines → Contributor workflow](16-guidelines.md#contributor-workflow) — normative branch-targeting rules. diff --git a/docs/addon-development/15-whats-new.md b/docs/addon-development/15-whats-new.md new file mode 100644 index 000000000..fec2379da --- /dev/null +++ b/docs/addon-development/15-whats-new.md @@ -0,0 +1,79 @@ +# What's New + +[← Previous](14-compatibility.md) · [Index](01-overview.md) · [Next →](16-guidelines.md) + +## Overview + +API and convention changes that affect addon authors, per Gramps minor release. The audience is someone with a working addon on the previous version asking *"what do I need to know before I bump `gramps_target_version`?"* + +This page is the **addon-author slice** of the change log. It's not the full release notes — those live on the wiki proper. Entries here are filtered for things that affect: + +- the `gramps.gen.*` import surface, +- the plugin-registration surface (`_pluginreg.py`), +- the docgen and report APIs, +- per-addon translation / locale plumbing, +- the addon discovery and loading mechanism. + +For the practical *how to port* guidance — what to check on a cross-version port, when to maintain parallel branches — see [14-compatibility](14-compatibility.md). This page is the inventory; 14-compatibility is the procedure. + +## Gramps 6.1 + +Targeted from `maintenance/gramps61`; `master` until the 6.1.0 release. + +### Added + +- **Plugin discovery follows symlinks.** Symlinking a working-tree addon folder into the user plugin directory now works, with realpath-based dedup so cycles terminate. Commit [`9443dcbb30`](https://github.com/gramps-project/gramps/commit/9443dcbb30), with `_manager_symlinks_test.py` covering both the scan-via-symlink case and loop termination. The dev loop on Linux/macOS becomes *symlink once, edit in place*. (See [01-overview → Where addons live](01-overview.md#where-addons-live).) + +### Changed + +- **Windows toolchain migrated from MINGW64 to MSYS2 UCRT64.** Gramps' Windows build moved in PR [#2198](https://github.com/gramps-project/gramps/pull/2198) (merged 2026-04-19). MINGW64's Python target triple is rejected by orjson's `maturin` backend; the migration was forced. + - **Impact on addon authors:** Windows addon testing targets `maintenance/gramps61` and `master` only — Windows on 6.0 is not upstream-tested. See [14-compatibility → Windows toolchain migrated to UCRT64](14-compatibility.md#windows-toolchain-migrated-to-ucrt64). +- **GExiv2 version handling rewritten.** addons-source PR [829](https://github.com/gramps-project/addons-source/pull/829) rewrote how GExiv2's version is read and pinned. An addon's `requires_gi=[("GExiv2", "0.10")]` declaration may need adjustment; read the EditExifMetadata addon's GExiv2 code on the target branch before assuming a 6.0 pin transfers. See [14-compatibility → GExiv2 version handling rewritten](14-compatibility.md#gexiv2-version-handling-rewritten). +- **BSDDB-on-Windows test skip.** A skip rule for BSDDB on Windows landed on `maintenance/gramps61`. Addons exercising the BSDDB backend in tests need to account for its absence on Windows 6.1 (use `@unittest.skipUnless(...)`; see [07-testing → Skip cleanly](07-testing.md#skip-cleanly)). + +### Deprecated + +*None tracked here yet.* The authoritative reference for runtime deprecations is the source — search `gramps/gen/**/*.py` on the target branch for `DeprecationWarning`. See [14-compatibility → Reading the deprecation signal](14-compatibility.md#reading-the-deprecation-signal) for the recipe. + +### Removed + +*None tracked here yet.* + +## Gramps 6.0 + +The manual's baseline target. Addons declaring `gramps_target_version="6.0"` run on 6.0.x and are not loaded by 6.1 or later (and vice versa); see [14-compatibility → `gramps_target_version` semantics](14-compatibility.md#gramps_target_version-semantics). + +### Added + +- **SQLite became the default database backend.** New trees are SQLite-backed unless the user explicitly chooses BSDDB. Addons that do straight `gramps.gen.db.*` reads keep working unchanged — the abstraction holds — but addons that bypassed the abstraction (e.g. reaching into BSDDB-specific cursor APIs) need to migrate to the portable interface. + +### Changed + +- **Python 3.10+ minimum.** Older Pythons no longer run Gramps 6.0, which means addons can use modern type-hint syntax — `X | None` instead of `Optional[X]`, `list[X]` instead of `typing.List[X]` — without a compatibility shim. See [16-guidelines → Coding style](16-guidelines.md#coding-style). + +### Deprecated + +*Verify against the source.* `DeprecationWarning`s on `maintenance/gramps60` are the authoritative list. + +### Removed + +*None tracked here yet.* + +## Earlier releases + +The 5.x → 6.0 transition was a major release; many APIs changed and the maintenance window for addons targeting earlier minors is closing. The authoritative reference for cross-major changes is the [Gramps wiki's release-notes pages](https://www.gramps-project.org/wiki/index.php/Portal:Using_Gramps#Release_notes). + +Practical guidance: addons still targeting 5.x should pin `gramps_target_version="5.2"` (the last 5.x minor) and live on the matching `addons-source` branch; the cross-major port is a separate exercise from the per-minor deltas this page tracks. + +## How to read this page + +- Each release section is **incremental** — entries describe what changed *from the previous minor*, not the cumulative API surface. +- Where an entry has an upstream commit, PR, or addon-side fix, it's cited inline so the change is auditable. Entries without a citation reflect conventions that emerged rather than discrete commits. +- The *current* surface (what's available right now) lives in [06-api-reference](06-api-reference.md), not here. + +## See also + +- [14-compatibility](14-compatibility.md) — porting an addon across these releases; the practical companion to this inventory. +- [06-api-reference](06-api-reference.md) — the current `gramps.gen.*` surface. +- [Portal:Using Gramps → Release notes](https://www.gramps-project.org/wiki/index.php/Portal:Using_Gramps#Release_notes) — upstream release notes (full, not addon-filtered). +- [`gramps/NEWS`](https://github.com/gramps-project/gramps/blob/maintenance/gramps61/NEWS) — the in-tree change log on the target branch. diff --git a/docs/addon-development/16-guidelines.md b/docs/addon-development/16-guidelines.md new file mode 100644 index 000000000..fa427ad14 --- /dev/null +++ b/docs/addon-development/16-guidelines.md @@ -0,0 +1,183 @@ +# Rules + +[← Previous](15-whats-new.md) · [Index](01-overview.md) · [Next →](17-roadmap.md) + +## Overview + +Normative reference for addon authors. Conceptual / how-to material lives in the other section pages; this page enumerates the guidelines and is the one to cite in code review. + +## Repository scope + +- **This page applies to the addon repository — [`gramps-project/addons-source`](https://github.com/gramps-project/addons-source).** It does **not** govern Gramps core. +- Core contributions (`gramps-project/gramps`) follow the separate [Core Development — Rules](https://gramps-project.org/wiki/index.php/Gramps_6.1_Wiki_Manual_-_Core_Development_-_Rules) page. The two repositories diverge on branch target, test layout, translation tooling, and which static checks are enforced — do not transfer a rule across without checking it here. +- The full Python coding standard is inherited from core's `../gramps/AGENTS.md`; this page restates the parts addon code review enforces and adds the addon-specific structure, packaging, and translation rules that live outside that file. +- **When in doubt, the authoritative source wins and is what to check.** These pages are a convenience restatement. On coding style, core's `../gramps/AGENTS.md` is the source of truth; on addon-specific rules, the authority is upstream `addons-source` (its `CONTRIBUTING.md` and a maintainer's ruling on the PR). Where this page is silent, ambiguous, or disagrees with the authoritative source on the *target branch*, that source wins — verify against it rather than relying on this page from memory. +- **Core stands in where this page doesn't — one way only.** Where this page is not specific or prescriptive on a point, the [Core Development — Rules](https://gramps-project.org/wiki/index.php/Gramps_6.1_Wiki_Manual_-_Core_Development_-_Rules) page (and core's `AGENTS.md`) is the default that fills the gap — addons inherit from core. The fallback runs in this direction only: where this page *is* prescriptive on an addon-specific concern (structure, packaging, branch target, test layout — `tests/` + `test_*.py`, `maintenance/gramps60`), it governs and core does not override it; and the addon guidelines never fill a gap in the core page. + +## Conventions + +RFC 2119 keywords, with our short forms: + +| Keyword | Meaning | +|---------|---------| +| **MUST** / **MUST NOT** | Required; a violation is a defect | +| **SHOULD** / **SHOULD NOT** | Strongly recommended; deviate only with a stated reason | +| **MAY** | Allowed | + +Where a rule has a known origin — an upstream PR, a maintainer ruling, a Mantis bug — it's cited inline so the rule is auditable. + +## Structure + +- **MUST**: the addon's folder name is a valid Python import name (an importable identifier — no spaces). Gramps puts each addon's directory on `sys.path` and addons share code via `import ` (see [the upstream Addons development page](https://gramps-project.org/wiki/index.php/Addons_development) → "name your addons with a name appropriate for Python imports"). The folder name need **not** match the `id` in `.gpr.py`: the registration `id` is an independent plugin key and routinely differs (e.g. folder `DeepConnectionsGramplet` ↔ id `Deep Connections Gramplet`), and one folder may register several plugins with unrelated ids. +- **MUST**: `.gpr.py` declares `gramps_target_version` matching the Gramps minor the addon targets. +- **MUST**: `fname` points to an implementation module shipped in the same folder. +- **MUST**: the addon is physically present under the plugin path — a physical copy works on every Gramps version and OS. (Gramps 6.1+ also discovers an addon reached via a symlink, but a physical copy is the portable default.) +- **MUST NOT**: import `register`, `GRAMPLET`, `STABLE`, `_`, or any other name Gramps injects into the `.gpr.py` namespace. +- **MUST NOT**: add `__init__.py` to the addon directory itself. The plugin loader puts the addon dir on `sys.path` and imports `.py` by name; making the addon dir a regular package disturbs that resolution and can trigger the [Mantis 12691](https://gramps-project.org/bugs/view.php?id=12691) submodule-binding trap. (See [07-testing → Why `tests/__init__.py` exists](07-testing.md#why-tests__init__py-exists).) +- **MUST** (`TOOL` kind): register an `optionclass` even when the tool takes no options. Gramps refuses to load a `TOOL` without one; an empty `tool.ToolOptions` subclass is sufficient. +- **SHOULD**: ship a `po/` directory with at least `template.pot` if any user-visible string exists. Generate it with `make.py init ` (see [12-packaging](12-packaging.md)); if it's missing the maintainer creates it on initial check-in. +- **MAY**: ship a `tests/` package with an `__init__.py` marker and at least one test — most existing addons predate addon unit tests. When tests are shipped, the `__init__.py` marker keeps dotted-path loading deterministic and the layout rules under *Testing* apply; a bug fix still **SHOULD** ship a regression test. +- **MAY**: ship multiple plugin kinds from a single addon — multiple `register(...)` calls in one `.gpr.py`, and/or multiple `.gpr.py` files in the addon folder (the loader scans every `*.gpr.py`). + +## Source location + +- **MUST**: edit addon source in `addons-source/`, never in the live plugin directory. The auto-sync runs source → installed plugin one-way; edits in the live dir are silently overwritten on the next source save. + +## Translation + +The full how-to (registration setup, `make.py` lifecycle, Glade runtime-override pattern, function reference) lives in [11-internationalization](11-internationalization.md). The rules below are what code review enforces. + +- **MUST**: wrap every user-visible string with `_()`. +- **MUST NOT**: `import _` in `.gpr.py` — Gramps' plugin loader injects it. Implementation modules **MUST** bind it explicitly via `_ = glocale.get_addon_translator(__file__).gettext`. +- **MUST** (multi-file packages): when the addon's code is split across a nested package, bind `_` **once at the addon root** — the directory that holds `locale/`, in a root-level module (e.g. `_i18n.py`) — and import it everywhere else by **bare name** (`from _i18n import _`), **not** a `.`-prefixed path: the addon dir is on `sys.path` and its root is **not** a package (see *Structure* → MUST NOT `__init__.py`), so a root-level module imports directly, whereas `from ..i18n import _` raises `'' is not a package` at import time. `get_addon_translator(filename)` derives the catalog dir as `dirname(abspath(filename)) + "/locale"` (`gramps.gen.utils.grampslocale`), so a `get_addon_translator(__file__)` call from a nested module (e.g. `myaddon/views/tab.py`) resolves `myaddon/views/locale/`, which doesn't exist, and a non-English user silently gets the untranslated string. The flat `_ = glocale.get_addon_translator(__file__).gettext` form above is correct only because that module sits at the addon root; from a nested module, anchor the path at the root (e.g. `get_addon_translator(os.path.join(ADDON_ROOT, "_"))` — only `dirname(...)` is read, so the basename is an unused placeholder) instead of passing `__file__`. (NameSuite i18n-anchor fix, 2026-06-25.) +- **SHOULD**: verify an addon translation against an **addon-owned** msgid — one that appears only in the addon's `template.pot`, never a string that also exists in core (e.g. `"Given name"`). `get_addon_translator` returns the **core** translator with the addon catalog only as a *fallback*, so a core string renders translated whether or not the addon binding resolves — it cannot prove the fix. (Same fix: the original check used a core string and demonstrated nothing.) +- **MUST NOT**: wrap an f-string or `.format()` result in a translation function. `xgettext` cannot extract dynamically built strings. + - **Bad:** `_(f"User {name}")`, `_("User {}".format(name))` + - **Good:** `_("User %s") % name` +- **MUST** (Glade): translatable strings in `.glade` / `.ui` files are **not** picked up by the addon translation tooling — the extractor only sees Python. For each translatable Glade string, give the widget a meaningful `id`, mark the string with `translatable="yes"` (optionally with a `"context|"` prefix), and override the label at runtime in Python: `self.get_widget("place_name_label").set_label(_("place|Name:"))`. +- **SHOULD**: use `ngettext(singular, plural, n)` for plural forms. +- **SHOULD**: use the pipe-prefix form `_("Context|String")` whenever a word could carry multiple senses (e.g. `_("book|Title")` vs `_("person|Title")`). This is the convention used throughout `addons-source` and is what translators see in the `.po` file. The two-arg form `_(msg, context)` works equivalently. **MUST NOT** call `pgettext` or `sgettext` directly — go through `_`. +- **SHOULD**: use `N_("…")` to mark a string for extraction without translating it at call time (e.g. for module-level constants that are translated later when displayed). + +> Addons have no `POTFILES.in` to maintain by hand — the per-addon `po/template.pot` is regenerated by `make.py init ` (see [12-packaging](12-packaging.md)). Maintaining `po/POTFILES.in` / `POTFILES.skip` is a **core** rule; see the [Core Development — Rules](https://gramps-project.org/wiki/index.php/Gramps_6.1_Wiki_Manual_-_Core_Development_-_Rules) page. + +## Runtime + +- **MUST**: perform every database write inside a `DbTxn`: + ```python + with DbTxn(_("Adding example"), db) as trans: + db.add_person(person, trans) + ``` +- **MUST**: declare runtime imports in `requires_mod` using the *importable* module name (`PIL`), not the PyPI distribution name (`Pillow`). +- **MUST**: verify each `requires_mod` entry with `importlib.util.find_spec("")` on a system with the package installed before publishing. +- **MUST**: use `requires_gi` for GObject-Introspection bindings, with version strings. The version pin **must match what the code actually imports** at runtime — pins can drift between Gramps minors (e.g. GExiv2 handling was rewritten on `maintenance/gramps61` per addons-source PR 829), so verify the pin against the target branch's related code, not just the previous branch's working declaration. +- **MUST NOT**: mutate process-global state that Gramps' startup owns — run or quit the GTK main loop (`Gtk.main()` / `Gtk.main_quit()`), install screen-wide CSS / retheme the icon theme / change `Gtk.Settings`, replace `sys.excepthook`, call `locale.setlocale` or `gettext.install`, configure the root logger, leave permanent `sys.path` entries, or set `os.environ` keys. An addon is a guest in Gramps' process; the full startup surface with per-item alternatives is [04-fundamentals → The provided environment](04-fundamentals.md#the-provided-environment). +- **SHOULD**: use handles (`PersonHandle`, etc.) for internal traversal; reserve Gramps IDs (`I0001`, …) for user-facing display. Handles are internal and stable; Gramps IDs are user-editable and rewritten in bulk by the Reorder Gramps IDs tool. +- **SHOULD**: import only from `gramps.gen.*`. `gramps.gui.*` and `gramps.plugins.*` are internal to the shipped distribution and break across Gramps versions. +- **SHOULD**: use a module-level logger (`LOG = logging.getLogger(__name__)`); **MUST NOT** use `print()` for diagnostic output. +- **SHOULD**: raise existing exceptions from `gramps.gen.errors` and `gramps.gen.db.exceptions` before inventing a new class. +- **SHOULD**: raise `HandleError` for invalid or missing handles. +- **SHOULD**: compare backlink class names by string. `db.find_backlink_handles(handle)` yields `(class_name, handle)` tuples where `class_name` is `"Person"` / `"Family"` / … as a `str`, not the Python class — `if cls is Person:` always evaluates `False`. +- **MAY**: introduce a new exception class only when none of the existing ones accurately represent the error condition. + +## Testing + +- **MUST**: use stdlib `unittest` — never `pytest`. Gramps itself standardises on `unittest`, which keeps addon tests contributable upstream. +- **MUST**: name test files `test_*.py` and place them in a `tests/` package alongside the addon module. +- **MUST**: scope platform-specific tests with the correct prefix: + + | Prefix | Where it runs | + |--------|---------------| + | `test_*.py` | All platforms | + | `test_linux_*.py` | Linux only | + | `test_windows_*.py` | Windows only | + | `test_integration_*.py` | Linux only — full-pipeline / DB-backed | + +- **MUST**: tests run cleanly without the addon's `requires_mod` dependencies installed in the Python that runs them — mock at the import boundary, or skip cleanly with `@unittest.skipUnless(...)`. Mac contributors can't easily install addon deps into the Gramps Python, and there's no Gramps debug-mode on Mac. (Gary Griffin, 2026-05-16.) +- **MUST**: never call `gi.require_version` in addon modules or test files. At runtime Gramps pins Gtk/Gdk before any plugin loads (`gramps/grampsapp.py`, `gramps/gen/constfunc.py`); under test, the pins live once in addons-source's **repo-root** `tests/__init__.py` (addons-source PR 950) — the per-addon `tests/__init__.py` stays empty, and tests run from the repository root so the pinned environment holds. Redundant pins MAY be removed from files already being touched. A module-level pin passes unit tests but breaks inside Gramps as soon as the hardcoded pin and the running version diverge — see [07-testing → The GTK-pin contract](07-testing.md#the-gtk-pin-contract). +- **SHOULD**: ship a regression test with every bug fix that **fails pre-fix and passes post-fix**. Doc-only PRs are the only exception. (At PR level this hardens to a MUST-with-escape — the test, or an explicit "no test because X" rationale; see *Contributor workflow*.) +- **SHOULD**: prefer `example.gramps`-backed tests over mocked DBs for DB-traversal logic — real data has cross-typed backlinks and ID-normalisation shapes that mocks don't reproduce. +- **MAY**: ship mocked unit tests alongside real-DB tests as complementary coverage. + +## Coding style + +**The coding standard is core's `../gramps/AGENTS.md`, in full — this section lists only the addon deltas.** Black, Python 3.10+ type hints (`X | None`, `list[X]`), Sphinx docstrings, import grouping with comment headers, class-header navigation comments, the `cb_` callback prefix, handle/ID types from `gramps.gen.types` — all are specified there and apply to addon Python unchanged. They are **not** restated below; anything this section is silent on follows core. The deltas are only these: + +- **Enforcement is advisory, so the core standard's coding MUSTs read as SHOULDs here.** addons-source runs no `black` / `mypy` / pylint gate — the reviewer weighs the standard; CI does not block on it. You **SHOULD** still run `black --check` before pushing, so the maintainer's cherry-pick forward to gramps61 stays clean. +- **Two rules are not softened — they stay MUST despite the lighter gate:** every new `.py` file carries a GPL-2.0-or-later license header with copyright, and every user-visible string is wrapped with `_()` (§Translation). +- **`gen`-self-containment, reframed.** Core's MUST that `gramps.gen.*` import no other submodule has no direct addon analog, but addon code **SHOULD** uphold the same discipline against itself: factor pure logic into modules that don't import `gramps.gui.*`, so it stays unit-testable without a display. + +## Contributor workflow + +- **MUST**: one logical fix per PR. Bundling hides mistakes. +- **MUST**: target the right branch — addon changes (`addons-source`) → `maintenance/gramps60`. The maintainer cherry-picks forward to `gramps61`. (Gary Griffin on addons-source PR 915, 2026-05-24.) A reviewer's instruction on a specific PR wins over the default targeting. (e.g. Nick-Hall on gramps#2299.) Core changes target a different branch — see the [Core Development — Rules](https://gramps-project.org/wiki/index.php/Gramps_6.1_Wiki_Manual_-_Core_Development_-_Rules) page. +- **MUST**: branch from `upstream/`, not the fork's tracking copy — fork bases drift (e.g. PRs 2315/2316 carried a stray `AGENTS.md` from the fork). +- **MUST NOT**: bump the addon's `version` field in an addons-source PR. The maintainer manages versions centrally. (Caught on PR 911, bug 12572.) +- **MUST**: a bug-fix PR includes a regression test, or an explicit "no test because X" rationale plus a manual repro. "Add the test later" is not an option. +- **MUST**: open the PR body with a **`**User impact:**`** line (before Root cause), then structure it **Summary / What to look at / Root cause / Fix / Verification**, citing `path:lines` on the branch the PR targets in the Verification "Checked" line (the #106 format). +- **MUST**: when the PR modifies an addon, **call out its current maintainer** — add an `## Affected addon` section to the PR body that **@-mentions the addon's current maintainer**, a heads-up so they are *aware* of the change and don't miss it. This is awareness, not attribution. "Current maintainer" = the addon's `.gpr.py` `maintainers` field when declared, otherwise its `authors` (an addon with no separate maintainer is maintained by its original author — Doug's "original developer (or contributors)" and Nick's "current maintainer" are the same role). The `.gpr.py` records names/emails, not GitHub handles — resolve a handle best-effort from the declared email so the mention notifies, and name the person when no handle resolves. (Raised on addons-source PR #946 — Doug Blank: *"otherwise I could miss fixes to my addons"*; Nick Hall: *"mention the current maintainer if one exists."*) +- **OPTIONAL**: reference the Mantis bug in the PR body **when one exists** — a Mantis reference is optional for addons-source, since many addon fixes have no Mantis ticket (they're tracked as fork GitHub issues, or are ticketless). addons-source also does not use the `Fixes #NNNN` commit-message trailer at all — that is the core convention; see the [Core Development — Rules](https://gramps-project.org/wiki/index.php/Gramps_6.1_Wiki_Manual_-_Core_Development_-_Rules) page. +- **MUST**: keep upstream-repo cross-references out of PR text and fork issues — reference *other* upstream PRs/issues in **plain text** ("upstream PR 949"), never a GitHub URL or `owner/repo#NNN` cross-ref (it back-links/notifies that thread). The `#nnnn` Mantis reference and the PR's own target are exempt. Authoritative: `docs/INTEGRATION.md` §"No upstream-repo links". +- **MUST NOT**: merge across branches. Rebase rather than merge — PRs with merge commits are rejected upstream. +- **MUST NOT**: cosmetically update in-flight upstream PRs. Parity, "rebase is clean," and "branch is behind" are not reasons to force-push. Push only when a specific correctness issue needs fixing. +- **SHOULD**: before writing any fix, check upstream isn't ahead — merged history on the target branch AND `master`, *plus* closed and rejected PRs on the *affected file* (not just the bug number). Closed PRs are signal: a closed-unmerged PR with the same fix shape is the maintainer's "no." +- **SHOULD**: if a PR already exists for the bug, verify it instead of duplicating. Merged → confirm-and-close; open → review and defer to the maintainer; closed → treat as the maintainer's "no." +- **SHOULD**: reproduce against `example.gramps` first — it's the canonical fixture and "couldn't reproduce" is the most common reason a fix stalls in triage. +- **MAY**: open as a draft PR for early review or to publish work-in-progress; mark ready when the change is complete and the author has re-read the diff with fresh eyes. + +## Verification before commit + +- **MUST**: find a test procedure before committing — local run, dry-run, snippet check. Never commit untested changes. +- **MUST**: treat a green mechanical check (lint, `git cherry-pick` applies, build green, `py_compile` exits 0) as evidence of *that narrow check*, not of correctness. Name what the check verified and what it left unverified. +- **MUST**: after pushing a PR branch, watch the PR's CI checks until they finish (e.g. `gh pr checks --watch`). Local pre-commit catches static checks only; test failures surface in CI's actual unit-test run. + +## Commit messages + +Commit messages are parsed by scripts that update Mantis BT and generate the ChangeLog / News files for releases. Formatting must be followed precisely. + +- **MUST**: the first line is a short summary, **≤ 70 characters**. +- **MUST**: the description is separated from the summary by a single blank line, and wrapped at **80 characters**. +- **MUST**: describe the change from the user's perspective. Don't recap the diff — `git diff` exists. +- **SHOULD**: use complete sentences in the description. +- **MUST**: reference another commit by its **full hash**, not a short hash. GitHub auto-hyperlinks full hashes; short hashes in brackets do not link. +- **MUST**: the Mantis trailer is on the **last** line of the commit message, separated from the description by a single blank line. + +### Mantis trailer keywords + +To **resolve** a bug (closes it on commit): + +``` +Fixes #12345 +Fixed #12345 +Resolves #12345 +Resolved #12345 +Fixes #12345, #67890 +``` + +To **link** to a bug (cross-reference without closing): + +``` +Bug #12345 +Issue #12345 +Report #12345 +Bugs #12345, #67890 +``` + +Bare numbers (no `#`) and URLs both miss the auto-link — use the `#NNNN` form. Note this is the opposite of the convention *inside* MantisBT itself, where `#NNNN` auto-links to another Mantis issue and bare numbers are preferred; here, inside Git commit messages and GitHub PR bodies, `#NNNN` is what hooks the MantisBT scripts. + +For the trailer to wire up on Mantis, the Git **author** or **committer** has to be a developer on the Mantis bug tracker. The Git name must match the Mantis username or real name, or the Git email must match the Mantis email. + +### addons-source: bug reference in PR body + +addons-source PRs don't use `Fixes #NNNN` in the commit message — that trailer is the core convention. A Mantis bug reference in the PR body is **optional**: include it when the fix has a Mantis ticket, but many addon fixes have none (fork GitHub issue, or ticketless), and those need no reference. A present-but-malformed reference is still wrong. + +## See also + +- [Overview](01-overview.md) +- [Fundamentals](04-fundamentals.md) +- [Testing](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Addon_Testing) +- [Code analysis](10-code-analysis.md) +- [Packaging](12-packaging.md) +- `../gramps/AGENTS.md` — the full Python coding standard inherited here. +- [addons-source CONTRIBUTING.md](https://github.com/gramps-project/addons-source/blob/maintenance/gramps60/CONTRIBUTING.md) +- [Committing policies](https://www.gramps-project.org/wiki/index.php/Committing_policies) — upstream's commit-message + Mantis-trailer rules. diff --git a/docs/addon-development/17-roadmap.md b/docs/addon-development/17-roadmap.md new file mode 100644 index 000000000..bec8e65ff --- /dev/null +++ b/docs/addon-development/17-roadmap.md @@ -0,0 +1,106 @@ +# Roadmap + +[← Previous](16-guidelines.md) · [Index](01-overview.md) + +## Overview + +Forward-looking view of the addon-development surface — what's planned, what's in flight, what's slated for deprecation, and what open questions will eventually become rules. The audience is an addon author asking "what do I need to plan around?" + +This page is the **prospective** counterpart to [What's new](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Addon_Development_-_Whats_New), which is retrospective. An item moves from this page to *What's new* once it ships in a release. + +## How to read this page + +Each entry should answer four things: + +| Field | Meaning | +|-------|---------| +| **Status** | proposed / accepted / in-flight / shipped / deferred / rejected | +| **Target** | Gramps version (`6.1`, `6.2`, ...) or "unscheduled" | +| **Impact** | what addon authors need to do (rewrite / opt-in / nothing) | +| **Tracking** | PR / Mantis bug / wiki RFC / mailing-list thread | + +A roadmap entry without a tracking link is a wish, not a plan; either add the link or move the entry to a separate "ideas" section. + +## In flight + + + +- _none recorded yet_ + +## Accepted but not yet implemented + + + +- _none recorded yet_ + +## Deprecations and removals + + + +- _none recorded yet_ + +## Open questions + + + +- _none recorded yet_ + +## Deferred / rejected + + + +- _none recorded yet_ + +## Documentation roadmap + +The doc set itself is in flight. Pages with `managed: false` front-matter are draft stubs and will not appear in published output until promoted. Current draft state — flip to `managed: true` page by page as content lands: + +### Publishing-pipeline conventions (now supported) + +What `md2wiki.py` and `md2pdf.py` handle as of 2026-05-30 — pages authored with these conventions render correctly in both wikitext and PDF output. Verified by running both pipelines on [Fundamentals](04-fundamentals.md) (which contains an SVG embed + Obsidian-internal links). + +| Convention | Where converted | Notes | +|------------|-----------------|-------| +| `![[_media/foo.svg\|cap]]` Obsidian embed | `mdcommon.convert_obsidian_embeds` | Becomes `![cap](_media/foo.svg)` before pandoc | +| `[[Page]]` / `[[Page\|label]]` Obsidian-internal link | `mdcommon.convert_obsidian_internal_links` | Resolved via `mdcommon.build_title_map` (filename-stem → wiki title); unresolved targets error loudly | +| Markdown image with SVG src in PDF | `_preconvert_svgs` (md2pdf) | Pre-converted to PDF via `rsvg-convert` or `inkscape`; embeds natively in xelatex | +| Markdown image with relative path in PDF | `--resource-path` to pandoc | Resolved against the source file's directory | +| `[[File:_media/foo.svg]]` post-pandoc wikitext | `mdcommon.basenameify_file_refs` | Becomes `[[File:foo.svg]]` (MediaWiki's File: namespace is flat) | +| Media files alongside pages | `wikitransport.upload_if_changed` + `publish.upload_media_for` | SHA-1 dedup; uploaded BEFORE the page edit so refs never render red | +| HTML comments | `mdcommon.stash_html_comments` | Stashed around Obsidian preprocessors so syntax inside comments is not rewritten | + +What the pipeline already handled before these additions: +- `[label](wiki:Page_Name)` → wikitext `[[Page|label]]` / PDF anchor or external URL. +- `` template shims → raw `{{...}}` wikitext / dropped from PDF. +- YAML front-matter → `title`, `categories`, `managed`. +- Fenced code with language tags, tables. + +### Page-by-page state + +The section is substantive across all seventeen pages. Open deepening work: + +- [Tutorials](02-tutorials.md) — the screenshots for each tutorial's "Try it" closer are pending capture. +- [Data access](05-data-access.md) — worked examples for some API touch-points are still thin. +- [API Reference](06-api-reference.md) — needs periodic re-synchronisation against `gramps/gen/__init__.py` on the maintenance branch this manual targets. + +## See also + +- [What's new](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Addon_Development_-_Whats_New) — retrospective counterpart. +- [Compatibility](14-compatibility.md) — porting guidance once an item ships. +- [Mantis bug tracker](https://gramps-project.org/bugs) — feature requests and design discussions originate here. +- [Gramps mailing lists](https://gramps-project.org/contact/) — where larger design questions get hashed out. diff --git a/docs/addon-development/README.md b/docs/addon-development/README.md new file mode 100644 index 000000000..95686ba8a --- /dev/null +++ b/docs/addon-development/README.md @@ -0,0 +1,24 @@ +# Gramps Addon Development manual + +The addon authors' manual for Gramps. Start at +[01-overview.md](01-overview.md) — the overview and section map. + +## Pages + +- [Addon Development](01-overview.md) +- [Tutorials](02-tutorials.md) +- [Addon Kinds](03-addon-kinds.md) +- [Fundamentals](04-fundamentals.md) +- [Data access](05-data-access.md) +- [API Reference](06-api-reference.md) +- [Testing](07-testing.md) +- [Debug](08-debug.md) +- [Troubleshoot](09-troubleshoot.md) +- [Code Analysis](10-code-analysis.md) +- [Internationalization](11-internationalization.md) +- [Packaging](12-packaging.md) +- [Community](13-community.md) +- [Compatibility](14-compatibility.md) +- [What's New](15-whats-new.md) +- [Rules](16-guidelines.md) +- [Roadmap](17-roadmap.md) diff --git a/docs/addon-development/_media/addon-kinds-ui-map.svg b/docs/addon-development/_media/addon-kinds-ui-map.svg new file mode 100644 index 000000000..f5507e356 --- /dev/null +++ b/docs/addon-development/_media/addon-kinds-ui-map.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + Gramps main window — where each addon kind appears + + + + + + + + + + File + + IMPORT / EXPORT + + + Edit + + + View + + + Reports + + REPORT + + + Tools + + TOOL + + + Windows + Help + + + + + [ toolbar ] + + + + + Navigator + ▸ Dashboard + ▸ People + ▸ Relationships + ▸ Families + ▸ Events + ▸ Places + ▸ Geography + ▸ Sources + ▸ Citations + ▸ Repositories + ▸ Media + ▸ Notes + + + + Main view area + (content varies by selected Navigator category) + + + + I0001 John Doe 1850– + + I0002 Jane Smith 1853– + + I0003 ... + + + + + Sidebar + + [ gramplet ] + + [ gramplet ] + + [ gramplet ] + + + + Bottombar + + [ gramplet ] + + [ gramplet ] + + [ gramplet ] + + [ gramplet ] + + + + + + SIDEBAR + — one per Navigator category + + + + + VIEW + — alternative way to browse a category + + + + + QUICKVIEW + — right-click context menu on a row + + + + + RULE + — Edit ▸ Person Filter Editor ▸ Add Rule + + + + + MAPSERVICE + — Geography view tile source + + + + + GRAMPLET + — Dashboard, sidebar, or bottombar widget + + + + + + Kinds with no direct UI surface + + DOCGEN — output format used by reports + DATABASE — backend selected at tree creation + RELCALC — used by Relationships view (per locale) + THUMBNAILER — media thumbnail generator + CITE — citation formatter style + GENERAL — shared library / pluggable category + + + Schematic — relative positions match Gramps 6.0's default layout; not pixel-accurate. See chapter 04-addon-kinds for the registration constants and base classes per kind. + diff --git a/docs/addon-development/_media/data-model.dot b/docs/addon-development/_media/data-model.dot new file mode 100644 index 000000000..dae803fc4 --- /dev/null +++ b/docs/addon-development/_media/data-model.dot @@ -0,0 +1,80 @@ +// Gramps primary objects and the most-traversed relationships. +// Regenerate the SVG with: +// dot -Tsvg data-model.dot -o data-model.svg +// +// Convention: +// - Solid arrow with no label = direct handle list +// - Solid arrow labelled "Ref" = goes through a ref object +// carrying metadata (Role, child +// relation, etc.) +// - Dashed arrow = reverse direction reached via +// db.find_backlink_handles() +// +// Notes and Tags can be attached to any primary object; omitted from +// the diagram to keep arrows readable, called out in the caption. + +digraph data_model { + rankdir=LR + bgcolor="transparent" + pad=0.25 + nodesep=0.5 + ranksep=0.9 + splines=true + + node [ + shape=box, + style="rounded,filled", + fillcolor="#eef2f7", + color="#4a5b6e", + fontname="Helvetica", + fontsize=11, + margin="0.18,0.10" + ] + edge [ + fontname="Helvetica", + fontsize=9, + color="#4a5b6e", + fontcolor="#4a5b6e" + ] + + // ---- Primary objects ---- + person [label="Person"] + family [label="Family"] + event [label="Event"] + place [label="Place"] + citation [label="Citation"] + source [label="Source"] + repository [label="Repository"] + media [label="Media"] + + // ---- Person <-> Family (two role-distinct relationships) ---- + person -> family [label=" parent_of\n family_handle_list "] + family -> person [label=" ChildRef ", style=solid] + + // ---- Events via EventRef (carries Role) ---- + person -> event [label=" EventRef\n (Role) "] + family -> event [label=" EventRef\n (Role) "] + + // ---- Places ---- + event -> place [label=" place_handle "] + place -> place [tailport="s", headport="s", label=" enclosed_by "] + + // ---- Sourcing chain ---- + person -> citation [label=" CitationRef "] + family -> citation [label=" CitationRef "] + event -> citation [label=" CitationRef "] + place -> citation [label=" CitationRef "] + citation -> source [label=" source_handle "] + source -> repository [label=" RepoRef "] + + // ---- Media ---- + person -> media [label=" MediaRef "] + event -> media [label=" MediaRef "] + source -> media [label=" MediaRef "] + + // ---- Layout hints to control column order ---- + { rank=same; person; family } + { rank=same; citation; media } + { rank=same; source } + { rank=same; repository } +} diff --git a/docs/addon-development/_media/data-model.svg b/docs/addon-development/_media/data-model.svg new file mode 100644 index 000000000..119275175 --- /dev/null +++ b/docs/addon-development/_media/data-model.svg @@ -0,0 +1,168 @@ + + + + + + +data_model + + +person + +Person + + + +family + +Family + + + +person->family + + +  parent_of +  family_handle_list   + + + +event + +Event + + + +person->event + + +  EventRef +  (Role)   + + + +citation + +Citation + + + +person->citation + + +  CitationRef   + + + +media + +Media + + + +person->media + + +  MediaRef   + + + +family->person + + +  ChildRef   + + + +family->event + + +  EventRef +  (Role)   + + + +family->citation + + +  CitationRef   + + + +place + +Place + + + +event->place + + +  place_handle   + + + +event->citation + + +  CitationRef   + + + +event->media + + +  MediaRef   + + + +place:s->place:s + + +  enclosed_by   + + + +place->citation + + +  CitationRef   + + + +source + +Source + + + +citation->source + + +  source_handle   + + + +repository + +Repository + + + +source->repository + + +  RepoRef   + + + +source->media + + +  MediaRef   + + + diff --git a/docs/addon-development/_media/packaging-pipeline.dot b/docs/addon-development/_media/packaging-pipeline.dot new file mode 100644 index 000000000..666835231 --- /dev/null +++ b/docs/addon-development/_media/packaging-pipeline.dot @@ -0,0 +1,85 @@ +// Three-repo packaging pipeline: addons-source -> make.py -> addons -> user. +// Regenerate the SVG with: +// dot -Tsvg packaging-pipeline.dot -o packaging-pipeline.svg + +digraph packaging_pipeline { + rankdir=LR + bgcolor="transparent" + pad=0.25 + nodesep=0.4 + ranksep=0.55 + + node [ + shape=box, + style="rounded,filled", + fillcolor="#eef2f7", + color="#4a5b6e", + fontname="Helvetica", + fontsize=11, + margin="0.18,0.10" + ] + edge [ + fontname="Helvetica", + fontsize=9, + color="#4a5b6e", + fontcolor="#4a5b6e" + ] + + // Repos and stages. + subgraph cluster_source { + label="addons-source repository" + labelloc="b" + fontname="Helvetica" + fontsize=10 + fontcolor="#4a5b6e" + color="#9aacc0" + style="rounded,dashed" + margin=10 + + source [label="MyAddon/\nMyAddon.gpr.py\nMyAddon.py\npo/\ntests/"] + } + + make [ + label="make.py gramps60 build MyAddon\n(compile po, package files)", + shape=box, + style="filled", + fillcolor="#dfe7f1" + ] + listing [ + label="make.py gramps60 listing MyAddon\n(refresh listings JSON)", + shape=box, + style="filled", + fillcolor="#dfe7f1" + ] + + subgraph cluster_addons { + label="addons repository" + labelloc="b" + fontname="Helvetica" + fontsize=10 + fontcolor="#4a5b6e" + color="#9aacc0" + style="rounded,dashed" + margin=10 + + tgz [label="gramps60/download/\nMyAddon.addon.tgz"] + listings [label="gramps60/listings/\n*.json"] + } + + manager [label="Gramps in-app\naddon manager"] + user [ + label="User plugin dir\n~/.local/share/gramps/\ngramps60/plugins/MyAddon/", + fillcolor="#e6efe2", + color="#5a7251" + ] + + // Edges. + source -> make [label=" author edits "] + source -> listing [style=invis] // keep alignment + make -> tgz [label=" build "] + make -> listing [style=dotted, arrowhead=none] + listing -> listings [label=" listing "] + tgz -> manager [label=" HTTPS fetch "] + listings -> manager [label=" HTTPS fetch "] + manager -> user [label=" install /\n update "] +} diff --git a/docs/addon-development/_media/packaging-pipeline.svg b/docs/addon-development/_media/packaging-pipeline.svg new file mode 100644 index 000000000..837a972a5 --- /dev/null +++ b/docs/addon-development/_media/packaging-pipeline.svg @@ -0,0 +1,124 @@ + + + + + + +packaging_pipeline + +cluster_source + +addons-source repository + + +cluster_addons + +addons repository + + + +source + +MyAddon/ +MyAddon.gpr.py +MyAddon.py +po/ +tests/ + + + +make + +make.py gramps60 build MyAddon +(compile po, package files) + + + +source->make + + +  author edits   + + + +listing + +make.py gramps60 listing MyAddon +(refresh listings JSON) + + + + +make->listing + + + + +tgz + +gramps60/download/ +MyAddon.addon.tgz + + + +make->tgz + + +  build   + + + +listings + +gramps60/listings/ +*.json + + + +listing->listings + + +  listing   + + + +manager + +Gramps in-app +addon manager + + + +tgz->manager + + +  HTTPS fetch   + + + +listings->manager + + +  HTTPS fetch   + + + +user + +User plugin dir +~/.local/share/gramps/ +gramps60/plugins/MyAddon/ + + + +manager->user + + +  install / +  update   + + + diff --git a/docs/addon-development/_media/plugin-discovery.dot b/docs/addon-development/_media/plugin-discovery.dot new file mode 100644 index 000000000..bb0179072 --- /dev/null +++ b/docs/addon-development/_media/plugin-discovery.dot @@ -0,0 +1,40 @@ +// Plugin discovery and load sequence. +// Regenerate the SVG with: +// dot -Tsvg plugin-discovery.dot -o plugin-discovery.svg + +digraph plugin_discovery { + rankdir=TB + bgcolor="transparent" + pad=0.2 + nodesep=0.4 + ranksep=0.5 + + node [ + shape=box, + style="rounded,filled", + fillcolor="#eef2f7", + color="#4a5b6e", + fontname="Helvetica", + fontsize=11, + margin="0.18,0.10" + ] + edge [ + fontname="Helvetica", + fontsize=9, + color="#4a5b6e", + fontcolor="#4a5b6e" + ] + + startup [label="Gramps startup"] + reg [label="reg_plugins(plugin_dir)\nrecursive scan\n(follows symlinks since 6.1)"] + gpr [label="Load each *.gpr.py\n(top-level register() calls execute)"] + catalog [label="Plugin catalog\nname, id, kind, target version\n(implementation module NOT loaded yet)"] + invoke [label="User invokes the addon\n(menu, sidebar, restart, ...)"] + load [label="Load fname module\nInstantiate the registered class"] + + startup -> reg + reg -> gpr [label=" for each\n addon folder"] + gpr -> catalog [label=" register(...)"] + catalog -> invoke [style=dashed, label=" later,\n on demand", constraint=false] + invoke -> load +} diff --git a/docs/addon-development/_media/plugin-discovery.svg b/docs/addon-development/_media/plugin-discovery.svg new file mode 100644 index 000000000..185d4468c --- /dev/null +++ b/docs/addon-development/_media/plugin-discovery.svg @@ -0,0 +1,90 @@ + + + + + + +plugin_discovery + + +startup + +Gramps startup + + + +reg + +reg_plugins(plugin_dir) +recursive scan +(follows symlinks since 6.1) + + + +startup->reg + + + + + +gpr + +Load each *.gpr.py +(top-level register() calls execute) + + + +reg->gpr + + +  for each +  addon folder + + + +catalog + +Plugin catalog +name, id, kind, target version +(implementation module NOT loaded yet) + + + +gpr->catalog + + +  register(...) + + + +invoke + +User invokes the addon +(menu, sidebar, restart, ...) + + + +catalog->invoke + + +  later, +  on demand + + + +load + +Load fname module +Instantiate the registered class + + + +invoke->load + + + + + From 813e841ec60041b4da51c882c7d105556a6a6f97 Mon Sep 17 00:00:00 2001 From: "Eduard R." Date: Mon, 20 Jul 2026 01:58:38 +0200 Subject: [PATCH 052/156] Point README and CONTRIBUTING at the in-repo addon manual README: the develop-your-own-addon pointer now leads to the in-repo docs/addon-development manual first, with CONTRIBUTING.md for the contributor workflow and the wiki page as an alternative rendering. The dead Travis badge is dropped. CONTRIBUTING: the deep technical sections that the manual now covers - addon kinds, registration and GENERAL plugins, prerequisites, addon configuration, localization, distribution contents, report categories, and the wiki listing/documentation templates - are reduced to a short retained summary plus a link into the manual, each under its original heading so existing deep links keep resolving. The contributor-workflow content that is unique to this document - repository and fork setup, development branches, the addon checklist, the pull-request walkthrough, and the maintenance guidance - is kept in place unchanged. Also repairs pre-existing broken links: seven table-of-contents and overview anchors that never matched their headings, two (#https://...) hrefs, the garbled Addon-list-legend link, and the Localization snippet that had lost its underscore binding. Depends on the PR that adds docs/addon-development. --- CONTRIBUTING.md | 619 +++++++++++------------------------------------- README.md | 4 +- 2 files changed, 145 insertions(+), 478 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dda23171b..702156efa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,13 +23,21 @@ If you're looking for *existing* addons to install, see If you're looking to contribute to Gramps directly, see [Portal:Developers](https://gramps-project.org/wiki/index.php/Getting_started_with_Gramps_development). +This document is the contributor **workflow** guide: repository setup, the +development loop, the checklist, and the pull-request process. The full +technical reference is the in-repo +[Addon Development manual](docs/addon-development/README.md) — seventeen +pages from a first Gramplet through testing, debugging, packaging, and +cross-version compatibility. Where a section below has a deeper counterpart +in the manual, it links there instead of repeating it. + ## Table of Contents * [What Can Addons Extend?](#what-can-addons-extend) * [Overview of Writing an Addon](#overview-of-writing-an-addon) * [Develop Your Addon](#develop-your-addon) - * [Addons Source Code Repository](#addon-source-code-repository) + * [Addons Source Code Repository](#addons-source-code-repository) * [Addons Download Repository](#addons-download-repository) - * [Set Up a Github Account](#setup-a-github-account) + * [Set Up a Github Account](#set-up-a-github-account) * [Create Project Forks in Github](#create-project-forks-in-github) * [Set Up Addon Development Environment](#set-up-addon-development-environment) * [Gramps Repository](#gramps-repository) @@ -50,7 +58,7 @@ If you're looking to contribute to Gramps directly, see * [Review the Addon Checklist](#review-the-addon-checklist) * [Create a Pull Request](#create-a-pull-request) * [Commit Your Changes](#commit-your-changes) - * [Verify Your Addon Is Current](#verify your addon is current) + * [Verify Your Addon Is Current](#verify-your-addon-is-current) * [Push To Your Fork](#push-to-your-fork) * [Create the PR](#create-the-pr) * [Work Towards a Merge](#work-towards-a-merge) @@ -60,47 +68,21 @@ If you're looking to contribute to Gramps directly, see * [List Your Addon](#list-your-addon) * [Document Your Addon](#document-your-addon) * [Support Your Addon Through Bug Tracker](#support-your-addon-through-bug-tracker) -* [Maintain Your Addon Code as Gramps Evolves](#maintain-your-addon-code-as-gramps-evolves) +* [Maintain Your Addon Code as Gramps Evolves](#maintain-your-addon-as-gramps-evolves) * [Resources](#resources) * [Addon Development Tutorials and Samples](#addon-development-tutorials-and-samples) * [Addons External to Github](#addons-external-to-github) ## What Can Addons Extend? - -Addons for Gramps can extend the program in many different ways. You can -add any of the following [types](https://github.com/gramps-project/gramps/blob/master/gramps/gen/plug/_pluginreg.py) of addons: - -* **Importer** (IMPORT) - adds additional file format import options to Gramps -* **Exporter** (EXPORT) - adds additional file format export options to Gramps -* **[Gramplet](https://gramps-project.org/wiki/index.php/Gramps_Glossary#gramplet)** (GRAMPLET) - adds a new -interactive interface section to a Gramps view mode, which can be -activated by right-clicking on the dashboard View or from the menu -of the Sidebar/Bottombar in other view categories. -* **Gramps** [**View mode**](https://gramps-project.org/wiki/index.php/Gramps_Glossary#viewmode) (VIEW) - adds a -new view mode to the list of views available within a -[View Category](https://gramps-project.org/wiki/index.php/Gramps_Glossary#view) -* **[Map Service](https://gramps-project.org/wiki/index.php/[Map_Services)** -(MAPSERVICE) - adds new mapping options to Gramps -* **Plugin lib** (GENERAL) - libraries that provide extra functionality when -present; can add, replace and/or modify builtin Gramps options. -* **[Quickreport/Quickview](https://gramps-project.org/wiki/indew/Gramps_6.0_Wiki_Manual_-_Reports_-_part_8#Quick_Views)** (QUICKREPORT) - a view -that you can run by right-clicking on an object, or if a person quickview, -then via the Quick View Gramplet -* **[Report](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Reports_-_part_1)** (REPORT) - adds a new output report; this includes -**Website** that outputs a static genealogy website based on your Gramps -Family Tree data. -* **[Rule](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Filters#Add_Rule_dialog)** (RULE) - adds new -[filter](https://gramps-project.org/wiki/index.php/Gramps_Glossary#filter) -rules. New starting with Gramps 5.1. -* **[Tool](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Tools)** (TOOL) - adds a utility that helps process data from your family tree. -* **Doc creator** (DOCGEN) -* **Relationships** (RECALC) -* **Sidebar** (SIDEBAR) -* **[Database](https://gramps-project.org/wiki/index.php/Database_Backends)** -(DATABASE) - add support for another database backend. New starting with -Gramps 5.0. -* **Thumbnailer** (THUMBNAILER) New starting with Gramps 5.2 -* **Citation formatter** (CITE) New starting with Gramps 5.2 +Addons can extend Gramps at almost every plugin point: Importers and +Exporters, Gramplets, View modes, Map Services, plugin libraries (GENERAL), +Quickviews, Reports, filter Rules, Tools, document generators (DOCGEN), +relationship calculators, Sidebars, Database backends, Thumbnailers, and +Citation formatters. + +The full catalogue — with the registration constant, UI location, base +class, and per-kind notes for each — is in the manual: +[Addon Kinds](docs/addon-development/03-addon-kinds.md). ## Overview of Writing an Addon Writing an addon is fairly straightforward if you have a bit of Python @@ -110,11 +92,11 @@ general steps to writing and sharing your own addons are: * [Develop your addon](#develop-your-addon) * [Create a Gramps plugin registration file](#create-a-gramps-plugin-registration-file) - e.g., a file named ```my-addon.gpr.py``` * [Review the Addon Checklist](#review-the-addon-checklist) -* [Create a Pull Request for your addon](#create-pr) -* [Announce it on the Gramps forum](#announce-the-addon) - Let users +* [Create a Pull Request for your addon](#create-a-pull-request) +* [Announce it on the Gramps forum](#announce-your-addon) - Let users know it exists and how to use it. -* [Support it through the issue tracker](#support-it-through-issue-tracker) -* [Maintain the code](#maintain-the-code-as-gramps-continues-to-evolve) as +* [Support it through the issue tracker](#support-your-addon-through-bug-tracker) +* [Maintain the code](#maintain-your-addon-as-gramps-evolves) as the Gramps code continues to evolve We'll now expand on each of these steps. @@ -463,24 +445,24 @@ see [Writing a Plugin](https://gramps-project.org/wiki/index.php/Writing_a_Plugin) specifically. +The manual walks these end-to-end: +[Tutorials](docs/addon-development/02-tutorials.md) builds one addon per +kind, and [Fundamentals](docs/addon-development/04-fundamentals.md) covers +the cross-cutting basics every kind shares. ### Test Your Addon As You Develop -To test your addon as you develop, we recommend you copy your -```NewProjectName``` plugin into your Gramps user plugin directory -from your addon development directory, prior to testing. Or, just -edit in the Gramps user plugin directory until it is ready to publish, -then copy back to your addon development directory. Your installed Gramps -desktop application will search this folder (and subdirectories) for -any ```.gpr.py``` files, and add them to the plugin list. - -You can of course still use the ```git``` branch for your addon to store -intermediate steps and other work in progress. - -> #### Warning -> [Bug #10436](https://gramps-project.org/bugs/view.php?id=10436) -> Symbolic links to folders in the gramps plugin directory are not scanned, so -> you cannot just create a symbolic link pointing to your addon source tree; -> you will have to copy it. +To test your addon as you develop, copy your ```NewProjectName``` folder +into the Gramps user plugin directory and restart Gramps — plugin discovery +happens at startup. On Gramps 6.0, discovery does **not** follow symbolic +links ([Bug #10436](https://gramps-project.org/bugs/view.php?id=10436)), so +a physical copy is required; Gramps 6.1 and later follow symlinks, so you +can link your working tree in once and edit in place. + +The manual covers the full development loop — where addons live, the +restart cycle, and a first working Gramplet — in +[the overview](docs/addon-development/01-overview.md#where-addons-live), +and how to test logic without launching the GUI in +[Testing](docs/addon-development/07-testing.md). If you have code that you want to share between addons, you don't need to do anything special. Gramps adds each directory in which a ```.gpr.py``` @@ -490,123 +472,42 @@ should always make sure you name your addons with a name appropriate for Python imports. ### Addon Configuration -Some addons may want to have persistent data (data settings that remain -between sessions). You can handle this yourself, or you can use Gramps' -builtin configure system. - -At the top of the source file for your addon, you would do this: -``` - from config import config as configman - config = configman.register_manager("grampletname") - # register the values to save: - config.register("section.option-name1", value1) - config.register("section.option-name2", value2) - ... - # load an existing file, if one: - config.load() - # save it, it case it didn't exist: - config.save() -``` - -This will create the file ```grampletname.ini``` and put it in the same -directory as the addon. If the config file already exists, it remains intact. -The natural location for ```.ini``` files is in the directory in which -the addon is installed; using the main ```gramps.ini``` file for addon -preferences could potentially lead to a conflict between addons. Other -locations and file formats are possible. See -[The Gramps architect recommends leaving this decision to the addon developer](https://gramps.discourse.group/t/add-option-for-boolean-options-in-gramplet/6371/19). - -In the addon, you can then: -``` - x = config.get("section.option-name1") - config.set("section.option-name1", 3) -``` - -and when this code is exiting, you might want to save the config. In a -Gramplet that would be: -``` - def on_save(self): - config.save() -``` - -If your code is a system-level file, then you might want to save the -config in the Gramps system folder: -``` - config = configman.register_manager("system", use_config_path=True) -``` - -This is rare; most ```.ini``` files go into the plugins directory. - -In other code that might use this config file, you would do this: -``` - from config import config as configman - config = configman.get_manager("grampletname") - x = config.get("section.option-name1") -``` +Some addons want persistent settings that survive between sessions. Use +Gramps' builtin configuration manager (```configman```) rather than rolling +your own file handling — the addon's ```.ini``` file lands in the addon's +own directory, so it cannot conflict with ```gramps.ini``` or with other +addons ([the Gramps architect recommends leaving the location decision to +the addon developer](https://gramps.discourse.group/t/add-option-for-boolean-options-in-gramplet/6371/19)). + +The full pattern — registering keys, load/save, the rare +```use_config_path``` system-folder case, and reading another addon's +settings with ```get_manager``` — is in the manual: +[Fundamentals → Configuration and persistent settings](docs/addon-development/04-fundamentals.md#configuration-and-persistent-settings). ### Localization - -> #### Note -> These instructions will only work for Python strings. If you have a -> glade file, it will not get translated. - -For general help with translations for Gramps, see -[Coding for translation](https://gramps-project.org/wiki/index.php/Coding_for_translation). However, that will only use translations that come with Gramps, -or allow you to contribute translations to the Gramps core. To have your own -managed translations that will be packaged with your addon, you will need to -add a way to retrieve the translation. Add the following to the top of your -```NewProjectName.py``` file: +Wrap every user-visible string in ```_()```, and bind the addon's own +translation catalog at the top of each implementation module: ``` from gramps.gen.const import GRAMPS_LOCALE as glocale - = glocale.get_addon_translator(__file__).gettext -``` -Then you can use the standard "```_()```" function to translate phrases in -your addon. - -You can use one of a few different types of translation functions: + _ = glocale.get_addon_translator(__file__).gettext ``` - gettext - lgettext - ngettext - lngettext - sgettext -``` - -These are obsolete starting in Gramps 4.x; ```gettext```, ```ngettext```, and -```sgettext``` always return translated strings in Unicode for consistent -portability between Python2 and Python3. - -See the [Python documentation](http://docs.python.org/3/library/gettext.html#the-gnutranslations-class) for using ```gettext``` and ```ngettext```. The -"l" versions return the string encoded according to the -[currently set locale](http://docs.python.org/3/library/locale.html#locale.setlocale); -the "u" versions return Unicode strings in Python2 and are no longer available -in Python3. +Glade files are **not** extracted automatically — mark their strings +translatable and override the labels at runtime in Python. -The method ```sgettext``` should always be used; it is a Gramps extension -that filters out clarifying comments for translators, such as in -```_("Remaining names | rest")``` where "rest" is the English string that -we want to present and "Remaining names" is a hint for translators. +The full workflow — string marking rules, plural forms, context prefixes +(```_("Remaining names|rest")```), ```.pot```/```.po``` generation with +```make.py```, and Weblate — is in the manual: +[Internationalization](docs/addon-development/11-internationalization.md). ### Files Included in Addon Distribution -The process that creates the compressed tar file that the Gramps Download -Manager installs in Gramps to use your addon automatically includes the -following files: -``` - *.py - *.glade - *.xml - *.txt - locale/*/LC_MESSAGES/*.mo -``` -Starting with Gramp 5.0, if you have files other than those listed above, -you should create a ```MANIFEST``` file in the root of your addon folder -that lists the files (or pattern) to be added, one per line, like this -sample ```MANIFEST``` file: -``` - README.md - extra_dir/* - help_files/docs/help.html -``` +The build automatically packages ```*.py```, ```*.glade```, ```*.xml```, +```*.txt```, and ```locale/*/LC_MESSAGES/*.mo```. Anything else (a +```README.md```, help files, extra directories) needs a ```MANIFEST``` +file in the addon root, one file or glob pattern per line (Gramps 5.0+). + +The build flow, ```MANIFEST``` semantics, and what lands in the +```.addon.tgz``` are in the manual: +[Packaging → What build packages](docs/addon-development/12-packaging.md#what-build-packages). > #### TIP > Starting with Gramps 6.0 (and _only_ 6.0) translations can be done on @@ -629,280 +530,65 @@ takes this general form: [PTYPE](https://github.com/gramps-project/gramps/blob/master/gramps/gen/plug/_pluginreg.py#L76) values include: TOOL, GRAMPLET, REPORT, QUICKVIEW (formerly QUICKREPORT), IMPORT, EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, RELCALC, SIDEBAR, DATABASE, RULE, -THUMBNAILER, and CITE. - -ATTR depends on the PTYPE. +THUMBNAILER, and CITE. ATTR depends on the PTYPE. -You must include a ```gramps_target_version``` and addon ```version``` values. -```gramps_target_version``` should be a string of the form "X.Y" matching -a Gramps X (major) and Y (minor) version. ```version``` is a string of -the form "X.Y.Z" representing the version of your addon; X, Y, and Z should -all be integers. +You must include ```gramps_target_version``` (a string "X.Y" matching the +Gramps major and minor version the addon targets) and the addon +```version``` (a string "X.Y.Z"). Include author name(s) and email(s) as +arrays of strings, and — new in Gramps 5.2 — optionally ```maintainers``` +/ ```maintainers_email``` when the maintainer differs from the author; the +maintainer is the primary point of contact. -Be sure to include attributes for author name(s) and email(s) in the form -of an array of comma-separated strings. +In the ```.gpr.py```, the function ```_``` is predefined by the plugin +loader to use your locale translations — mark text with ```_("TEXT")```, +never import ```_``` there. -There is an additional set of attributes, ```maintainers``` and -```maintainers_email``` (new in Gramps 5.2). If you, the author, are also -the maintainer it will be identical to the author attributes, but you may -also designate a maintainer, in which case the maintainer will become the -primary point of contact. - -Here is a sample Tool GPR file: -``` - register(TOOL, - id = 'AttachSource', - name = _("Attach Source"), - description = _("Attaches a shared source to multiple objects."), - version = '1.0.0', - gramps_target_version = '6.0', - status = STABLE, - fname = 'AttachSourceTool.py', - authors = ["Douglas S. Blank"], - authors_email = ["doug.blank@gmail.com"], - maintainers = ["Douglas S. Blank"], - maintainers_email = ["doug.blank@gmail.com"], - category = TOOL_DBPROC, - toolclass = 'AttachSourceWindow', - optionclass = 'AttachSourceOptions', - tool_modes = [TOOL_MODE_GUI], - help_url = "Addon:AttachSourceTool" - ) -``` - -You can see examples of the kinds of addons -[here](https://github.com/gramps-project/gramps/plugins) (such as -[gramps-project/gramps/plugins/drawreport/drawplugins.gpr.py](https://github.com/gramps-project/gramps/plugins/drawreport/drawplugins.gpr.py)) -and see the full documentation in the -[master/gramps/gen/plug/_pluginreg.py][https://github.com/gramps-project/gramps/blob/3f0db9303f29811b43325c30149c8844c7ce24b6/gramps/gen/plug/_pluginreg.py#L23) -comments and docstrings. - -Note that this example ```.gpr.py``` will automatically use translations -if you have them (see [Localization](#localization)). That is, the -function "_" is predefined to use your -locale translations; you only need to mark the text with ```_("TEXT")``` -and include a translation of "TEXT" in your translation file. For example, -in the above example, ```_("Attach Source")``` is marked for translation. -If you have developed and packaged your addon with translation support, -then that phrase will be converted into the user's language. +The manual documents every registration field, the discovery model, and a +complete example per kind: +[Fundamentals → The .gpr.py registration file](docs/addon-development/04-fundamentals.md#the-gprpy-registration-file) +and [Addon Kinds](docs/addon-development/03-addon-kinds.md). ### Report Plugins -The possible report categories are -[gramps/gen/plug/_pluginreg.py](https://github.com/gramps-project/gramps/blob/892fc270592095192947097d22a72834d5c70447/gramps/gen/plug/_pluginreg.py#L141-L149): -``` - #possible report categories - CATEGORY_TEXT = 0 - CATEGORY_DRAW = 1 - CATEGORY_CODE = 2 - CATEGORY_WEB = 3 - CATEGORY_BOOK = 4 - CATEGORY_GRAPHVIZ = 5 - CATEGORY_TREE = 6 - REPORT_CAT = [ CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE, - CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE] -``` - -Each report category has a set of standards and an interface. The categories -```CATEGORY_TEXT``` and ```CATEGORY_DRAW``` use the Document interface of -Gramps. See also -[Report API](https://gramps-project.org/wiki/index.php/Report_API) -for a draft view on this. - -The application programming interface or API for reports is treated in the -[report writing tutorial](https://gramps-project.org/wiki/index.php/Report-writing_tutorial). For general information on Gramps development, see -[Portal:Developers](https://gramps-project.org/wiki/index.php/Portal:Developers) -and [Writing a Plugin](https://gramps-project.org/wiki/index.php/Writing_a_plugin). +A REPORT registration declares one of the report categories +(```CATEGORY_TEXT```, ```CATEGORY_DRAW```, ```CATEGORY_WEB```, and so on — +defined in ```gramps/gen/plug/_pluginreg.py```); the text and draw +categories use Gramps' Document interface. + +The manual covers the Report/ReportOptions pair, the docgen abstraction, +and a complete text-report walkthrough: +[Addon Kinds → REPORT](docs/addon-development/03-addon-kinds.md) and +[Tutorials → A text Report](docs/addon-development/02-tutorials.md#a-text-report). +See also the +[report writing tutorial](https://gramps-project.org/wiki/index.php/Report-writing_tutorial) +on the wiki. ### General Plugins -The plugin framework also allows you to create generic plugins for use. -This includes the ability to create libraries of functions, and plugins -of your own design. - -#### Example: A library of functions -In this example, a file named ```library.py``` will be imported at the -time of registration (i.e., any time Gramps starts): -``` - # file: library.gpr.py - - register(GENERAL, - id = 'My Library', - name = _("My Library"), - description = _("Provides a library for doing something."), - version = '1.0', - gramps_target_version = '6.0', - status = STABLE, - fname = 'library.py', - load_on_reg = True, - ) -``` - -You can access the loaded module in other code by issuing an -```import library``` as Python keeps track of files already -imported. However, the amount of useful code that you can run -when the program is imported is limited. You might like to have -the code do something that requires a ```dbstate``` or ```uistate object```, -but neither of these is available when just importing a file. - -If ```load_on_reg``` was not ```True```, this code would be unavailable -until manually loaded. There is no mechanism in Gramps to load ```GENERAL``` -plugins automatically. - -In addition to importing a file at startup, you can also run a single -function inside a ```GENERAL``` plugin, and it will be passed the -```dbstate```, the ```uistate```, and the plugin data. The function -must be called ```load_on_reg```, and take those three parameters: -``` - # file: library.py - - def load_on_reg(dbstate, uistate, plugin): - """ - Runs when plugin is registered. - """ - print("Hello World!") -``` - -Here, you could connect signals to the ```dbstate```, open windows, etc. - -Another example of what you can do with the plugin interface is to create -a general purpose plugin framework for use by other plugins. Here is the -basis for a plugin system that: - -* allows plugins to list data files -* allows the plugin to process all of the data files - -First, the ```gpr.py``` file: -``` - register(GENERAL, - id = "ID", - category = "CATEGORY", - load_on_reg = True, - process = "FUNCTION_NAME", - ) -``` - -This example uses three new features: - -* ```GENERAL``` plugins can have a category -* ```GENERAL``` plugins can have a load_on_reg function that returns data -* ```GENERAL``` plugins can have a function (called ```process```) which -will process the data - -If you (or someone else) create additional general plugins of this category, -and they follow your ```load_on_reg``` data format API, then they could be -used just like your original data. For example, here is an additional -general plugin in the ```WEBSTUFF``` category: -``` - # anew.gpr.py - - register(GENERAL, - id = 'a new plugin', - category = "WEBSTUFF", - version = '1.0', - gramps_target_version = '6.0', - data = ["a", "b", "c"], - ) -``` - -This doesn't have ```load_on_reg = True```, nor does it have an ```fname``` or -```process```, but it does set the data directly in the ```.gpr.py``` file. -Then, we have the following results: -``` - >>> from gui.pluginmanager import GuiPluginManager - >>> PLUGMAN = GuiPluginManager.get_instance() - >>> PLUGMAN.get_plugin_data('WEBSTUFF') - ["a", "b", "c", "Stylesheet.css", "Another.css"] - >>> PLUGMAN.process_plugin_data('WEBSTUFF') - ["A", "B", "C", "STYLESHEET.CSS", "ANOTHER.CSS"] -``` +```GENERAL``` is the escape hatch for plugin code that doesn't fit any +other kind: shared function libraries (imported at startup with +```load_on_reg = True``` and then available to other addons via a plain +```import```), and pluggable categories such as ```WEBSTUFF``` (narrative +website stylesheets) and ```Filters``` (filter-rule providers). + +The manual documents both uses and the full plugin-data API — the +```load_on_reg(dbstate, uistate, plugin)``` function form, the ```data``` +and ```process``` registration fields, and querying by category through +the plugin manager: +[Addon Kinds → GENERAL](docs/addon-development/03-addon-kinds.md). ### Registered GENERAL Categories -The following are examples of the published secondary plugin categories of -APIs of type ```GENERAL```. - -#### WEBSTUFF -A sample ```gpr.py``` file: -``` - # stylesheet.gpr.py - - register(GENERAL, - id = 'system stylesheets', - category = "WEBSTUFF", - name = _("CSS Stylesheets"), - description = _("Provides a collection of stylesheets for the web"), - version = '1.0', - gramps_target_version = '6.0', - fname = "stylesheet.py", - load_on_reg = True, - process = "process_list", - ) -``` - -Here is the associated program: -``` - # file: stylesheet.py - - def load_on_reg(dbstate, uistate, plugin): - """ - Runs when plugin is registered. - """ - return ["Stylesheet.css", "Another.css"] - - def process_list(files): - return [file.upper() for file in files] -``` - -#### Filters -For example, ```gpr.py```: -``` - register(GENERAL, - category="Filters", - ... - load_on_reg = True - ) -``` -And the actual plugin: -``` - def load_on_reg(dbstate, uistate, plugin): - # returns a function that takes a namespace, 'Person', 'Family', etc. - - def filters(namespace): - print("Ok...", plugin.category, namespace, uistate) - # return a Filter object here - - return filters -``` +The published ```GENERAL``` categories — ```WEBSTUFF``` and ```Filters``` +— with sample registrations and implementations, are covered in +[Addon Kinds → GENERAL](docs/addon-development/03-addon-kinds.md). ### List Your Addon Prerequistes -In your ```.gpr.py``` file, you can have a line like: -``` - ... - depends_on = ["libwebconnect"], - ... -``` - -which is a list of plug-in identifiers from other ```.gpr.py``` files. -This example will ensure that -[libwebconnect](https://gramps-project.org/wiki/index.php/Addon:Web_Connect_Pack#Prerequisites) -is loaded before your addon. If that ID can't be found, or you have a cycle -(a circular import), then your addons won't be loaded. The Gramps architect -summarizes this as: "The ```depends_on``` list is used to specify other plugins -which the plugin depends on. These will be installed automatically." - -Example code in the Addon:Web_Connect_Pack that references ```libwebconnect``` -prerequistes can be seen in -[addons-source/RUWebPack.gpr.py#L17](https://github.com/gramps-project/addons-source/blob/1304b65a7d758bfe17339c26260473ac3e9c4061/RUWebConnectPack/RUWebPack.gpr.py#L17). +```depends_on = ["libwebconnect"]``` in a ```.gpr.py``` lists the ids of +other plugins that must load first (they are installed automatically); +```requires_mod```, ```requires_gi```, and ```requires_exe``` (Gramps 5.2+) +declare Python-module, GObject-introspection, and executable prerequisites. -This allows common prerequisites to be shared between addons. Code can be -maintained in its own ```.gpr.py```/addon file instead of trying to -synchronize the maintenance of multiple copies across various silos. - -Additional requirements properties were implemented starting with the -Gramps 5.2 -[Registration Options](https://gramps-project.org/wiki/index.php/Gramplets_development#Register_Options) that provide for specifying plug-in preqrequisites: - -* For modules: [```requires_mod```](https://github.com/gramps-project/gramps/blob/0f8d4ecd429431b4df64910962f8764af9ff1766/gramps/gen/plug/_pluginreg.py#L689-L719) -* For GObject introspection: [```requires_gi```](https://github.com/gramps-project/gramps/blob/0f8d4ecd429431b4df64910962f8764af9ff1766/gramps/gen/plug/_pluginreg.py#L689-L719) -* For executables: [```requires_exe```](https://github.com/gramps-project/gramps/blob/0f8d4ecd429431b4df64910962f8764af9ff1766/gramps/gen/plug/_pluginreg.py#L689-L719) +Declaration rules — importable module names, verifying entries, version +pins — are in the manual: +[Fundamentals → Declaring dependencies](docs/addon-development/04-fundamentals.md#declaring-dependencies). ## Review the Addon Checklist Before you publish your new addon, review this checklist for completeness: @@ -915,6 +601,10 @@ Before you publish your new addon, review this checklist for completeness: * Has the help_url been changed from the GitHub repository to the wiki page? +The normative MUST / SHOULD / MAY rules a reviewer holds an addon to — +structure, runtime, testing, translation, and the contributor workflow — +are in the manual: [Guidelines](docs/addon-development/16-guidelines.md). + ## Create a Pull Request Once you have created your addon, built the ```.gpr.py``` registration file, and have tested it (you *did* test it, right?) so that you're sure @@ -1129,42 +819,30 @@ Now it is time to announce your addon to those who may not have heard about it yet. ### Gramps Forum -Join the [Gramps Forum](#https://gramps-project.org/wiki/index.php/Contact#Forum) if you have not already. Announce your addon to forum users with general -information on why you created it, what it does for the user, and how to use -it. +Join the [Gramps Forum](https://gramps.discourse.group/) if you have not +already. Announce your addon to forum users with general information on +why you created it, what it does for the user, and how to use it. ### Gramps Wiki Create an account on the -[Gramps Wiki](#https://gramps-project.org/wiki/index.php/Main_page) +[Gramps Wiki](https://gramps-project.org/wiki/index.php/Main_page) if you don't already have one. #### List Your Addon Add a short description of your addon to the Addons list in the wiki by -editing the current release listing: i.e., +editing the current release listing: i.e., [6.0_Addons](https://gramps-project.org/wiki/index.php/6.0_Addons), or if the addon is meant for a future release, [6.1_Addons](https://gramps-project.org/wiki/index.php/6.1_Addons) when available. Examine other addon entries when editing the wiki page, and refer to the -[Addon list legend]]](https://gramps-project.org/wiki/index.php/Addon_list_legend) -list legend]] to understand the meaning of each column. When ready, use the -following template to include your addon in the list: -``` -|- -| -| -| -| -| -| -| -| -|- -``` +[Addon list legend](https://gramps-project.org/wiki/index.php/Addon_list_legend) +to understand the meaning of each column. The row template to copy is in +the manual: [Community → List your addon](docs/addon-development/13-community.md#list-your-addon). #### Document Your Addon -Document your addon in the wiki using the page name format -**Addon:NewProjectName**. Examine some of the other addon documentaion +Document your addon in the wiki using the page name format +**Addon:NewProjectName**. Examine some of the other addon documentation pages for suggestions, and for the general format to use. > ##### TIP @@ -1173,30 +851,9 @@ pages for suggestions, and for the general format to use. > results page you will be provided with a link to create the new page. > Select that link to add your content. -Consider including the following information in your wiki page: - -``` - -{{Third-party plugin}} - - -== Usage == - -=== Configure Options === - -==Features== - -== Prerequisites == - -== Issues == - - -[[Category:Addons]] -[[Category:Plugins]] -[[Category:Developers/General]] -``` +The conventional page skeleton (the ```{{Third-party plugin}}``` banner and +the standard sections) is in the manual: +[Community → Document your addon](docs/addon-development/13-community.md#document-your-addon). ## Support Your Addon Through Bug Tracker Create a user account on the @@ -1233,6 +890,12 @@ often before being officially accepted. core, but are loved by many users (e.g., the Data Entry Gramplet). * A place for experimental components to live. +The technical side of keeping an addon working across Gramps releases — +```gramps_target_version``` semantics, the per-release deltas that bite +ports, and the porting checks — is in the manual: +[Compatibility](docs/addon-development/14-compatibility.md) and +[What's New](docs/addon-development/15-whats-new.md). + ### Examples of Common Enhancements And here are just some of the kinds of enhancements that might make sense: @@ -1277,6 +940,8 @@ Also you may want to [[Addons_development#Package_your_addon |Package your addon --> ## Resources +* [Addon Development manual](docs/addon-development/README.md) — the +in-repo technical reference this document links throughout. * [Brief introduction to Git](https://gramps-project.org/wiki/index.php/Brief_introduction_to_git) * [Getting started with Gramps development](https://gramps-project.org/wiki/index.php/Getting_started_with_Gramps_development) * [Portal:Developers](https://gramps-project.org/wiki/index.php/Portal:Developers) @@ -1289,6 +954,8 @@ Also you may want to [[Addons_development#Package_your_addon |Package your addon * For 4.1.x and earlier, see [Addons development old](https://gramps-project.org/wiki/index.php/Addons_development_old). ## Addon Development Tutorials and Samples +* [Tutorials](docs/addon-development/02-tutorials.md) — in-repo end-to-end +walkthroughs, one per addon kind (Gramplet, Tool, Report, Quick View, Rule). * [Develop an Addon Gramplet](https://gramps-project.org/wiki/index.php/Gramplets_development) (or add a [custom filtering option](https://gramps.discourse.group/t/looking-for-an-example-of-a-gramplet-with-a-custom-filter-configuration-option/5967)) * [Develop_an_Addon_Rule](https://gramps-project.org/wiki/index.php/Develop_an_Addon_Rule) for custom filters * [Develop_an_Addon_Tool](https://gramps-project.org/wiki/index.php/Develop_an_Addon_Tool) diff --git a/README.md b/README.md index 1f1fae31d..cd7ac8e66 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -addons-source [![Build Status](https://travis-ci.org/gramps-project/addons-source.svg?branch=master)](https://travis-ci.org/gramps-project/addons-source) +addons-source Translation status ============= Source code of contributed third-party addons for the [Gramps genealogy program](https://github.com/gramps-project/gramps). -You can develop your own addon following the [Addons Development](https://gramps-project.org/wiki/index.php?title=Addons_development) wiki. +You can develop your own addon following the in-repo [Addon Development manual](docs/addon-development/README.md); the contributor workflow (forks, branches, pull requests) is in [CONTRIBUTING.md](CONTRIBUTING.md). See also the [Addons Development](https://gramps-project.org/wiki/index.php?title=Addons_development) wiki page. Note: The default git branch is `master`. The master branch should only be used to develop addons that require features or changes found in the Gramps master branch. Most of the time addons should be developed to work with the current released version of Gramps (`maintenance/gramps60` for the Gramps 6.0.x versions for example). From e40e5192aff972542a77c7b327300fc47795f6f8 Mon Sep 17 00:00:00 2001 From: "Eduard R." Date: Mon, 20 Jul 2026 02:10:18 +0200 Subject: [PATCH 053/156] Strip trailing whitespace in Python source files The addons-source CI (PR 820) adds a lint step that fails on any tracked Python file carrying trailing whitespace. Three pre-existing files trip it (27 lines total): ArchiveAssist/ArchiveAssist.py (22), and two FilterRules modules (5). Strip the trailing whitespace so the gate can pass; whitespace only, no behavioural change (git diff -w is empty). --- ArchiveAssist/ArchiveAssist.py | 44 ++++++++++++------------ FilterRules/matcheventfilterrole.py | 8 ++--- FilterRules/matchparentoffilterfamily.py | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ArchiveAssist/ArchiveAssist.py b/ArchiveAssist/ArchiveAssist.py index 83f2aef86..ed581cab5 100644 --- a/ArchiveAssist/ArchiveAssist.py +++ b/ArchiveAssist/ArchiveAssist.py @@ -94,7 +94,7 @@ def parse_ref(text: str) -> dict: match = pattern.search(text) if not match: return {} - + archive = match.group("archive").strip() to_remove = ["kyrkoarkiv", "stadsarkiv"] for word in to_remove: @@ -127,7 +127,7 @@ def parse_ref(text: str) -> dict: # Gramplet class # ------------------------ class ArchiveAssist(Gramplet): - + # I am bad at GUI. Double check... def init(self): if getattr(self, "_initialized", False): @@ -170,7 +170,7 @@ def build_gui(self): vbox.pack_start(self.status_label, False, False, 0) return vbox - + def get_or_create_repository(self, name, trans): for handle in self.dbstate.db.get_repository_handles(): repo = self.dbstate.db.get_repository_from_handle(handle) @@ -195,11 +195,11 @@ def on_create_clicked(self, widget): if not parsed: self.status_label.set_text("Could not parse the reference string.") return - + # Find existing Source (by title) src = None src_handle = None - + for handle in self.dbstate.db.get_source_handles(): candidate = self.dbstate.db.get_source_from_handle(handle) for attr in candidate.get_attribute_list(): @@ -213,7 +213,7 @@ def on_create_clicked(self, widget): try: with DbTxn("Create Source and Citation", self.dbstate.db) as trans: - + # Source if src: self.status_label.set_text( @@ -223,14 +223,14 @@ def on_create_clicked(self, widget): src = Source() src.set_title(parsed["abr"]) src.set_publication_info(parsed["years"]) - + # FIXED NAD attribute nad_attr = Attribute() nad_attr.set_type("NAD") nad_attr.set_value(parsed["NAD"]) src.add_attribute(nad_attr) - - # FIXED AID attribute (if present) + + # FIXED AID attribute (if present) if parsed["AID"]: aid_attr = Attribute() aid_attr.set_type("AID") @@ -239,46 +239,46 @@ def on_create_clicked(self, widget): # First add the Source WITHOUT repo refs src_handle = self.dbstate.db.add_source(src, trans) - + # Now add RepoRef AFTER the source exists repo_ref = RepoRef() repo_handle = self.get_or_create_repository(parsed["provider"], trans) repo_ref.set_reference_handle(repo_handle) - + src.add_repo_reference(repo_ref) - + # Persist the updated Source with its RepoRef self.dbstate.db.commit_source(src, trans) - + # Citation if parsed["page"]: cit = Citation() cit.set_confidence_level(2) cit.set_page(parsed["page"]) - + years = parsed["years"] cit_date = Date() if years and "-" in years: start_year, end_year = [y.strip() for y in years.split("-")] cit_date.set( - modifier=Date.MOD_RANGE, + modifier=Date.MOD_RANGE, value=(0, 0, int(start_year), False, 0, 0, int(end_year), False)) cit.set_date_object(cit_date) elif years: cit_date.set_year(int(years.strip())) cit.set_date_object(cit_date) - - cit.set_reference_handle(src_handle) - + + cit.set_reference_handle(src_handle) + if parsed["full_AID"]: AID = Attribute() AID.set_type("AID") AID.set_value(parsed["full_AID"]) cit.add_attribute(AID) - + cit_handle = self.dbstate.db.add_citation(cit, trans) - + self.status_label.set_text( f"Created Source ({src.get_gramps_id()}) and Citation ({cit.get_gramps_id()})." ) @@ -287,7 +287,7 @@ def on_create_clicked(self, widget): f"Created Source ({src.get_gramps_id()}). No Citation created due to missing page info." ) - except Exception as e: + except Exception as e: LOG.error("ArchiveAssist failed: %s", str(e), exc_info=True) self.status_label.set_text( - "Failed to create Source/Citation. Check logs.") \ No newline at end of file + "Failed to create Source/Citation. Check logs.") \ No newline at end of file diff --git a/FilterRules/matcheventfilterrole.py b/FilterRules/matcheventfilterrole.py index 8c9cd983b..6e813aac9 100644 --- a/FilterRules/matcheventfilterrole.py +++ b/FilterRules/matcheventfilterrole.py @@ -54,7 +54,7 @@ def __init__(self, db): class MatchesEventFilterRole(MatchesEventFilter): labels = [_("Event filter name:"), _("Include Family events:"), (_('Role:'), Roletype)] name = _("Persons with events matching the with role") - + def prepare(self, db: Database, user): MatchesEventFilter.prepare(self, db, user) @@ -65,9 +65,9 @@ def prepare(self, db: Database, user): self.MPF_famevents = False except IndexError: self.MPF_famevents = False - + def apply_to_one(self, db: Database, person: Person) -> bool: - + filt = self.find_filter() if filt: for event_ref in person.get_event_ref_list(): @@ -89,4 +89,4 @@ def apply_to_one(self, db: Database, person: Person) -> bool: if filt.apply_to_one(db, event): return True return False - + diff --git a/FilterRules/matchparentoffilterfamily.py b/FilterRules/matchparentoffilterfamily.py index 0b202b61c..87d5dc1e5 100644 --- a/FilterRules/matchparentoffilterfamily.py +++ b/FilterRules/matchparentoffilterfamily.py @@ -62,7 +62,7 @@ class MatchesParentOfFilterFamily(MatchesFilterBase): # we want to have this filter show family filters namespace = "Family" - + def apply_to_one(self, db: Database, person: Person) -> bool: filt = self.find_filter() if filt: From 3eaf55764687b8777b60d7b38dd8e5bd087972a8 Mon Sep 17 00:00:00 2001 From: "Eduard R." Date: Mon, 20 Jul 2026 02:13:09 +0200 Subject: [PATCH 054/156] AssociationsTool: stop shadowing the gettext _ with a loop variable _build_with_progress used `for _ in range(batch_size)`, which makes _ a local variable throughout the whole method (Python binds it at compile time). That shadows the module-level `_ = _trans.gettext`, so: - the except handler at the top of the method, LOG.error(_("Error initializing data: %s") % str(e)), runs before the loop assigns _ and raises UnboundLocalError; and - the in-loop handler LOG.warning(_("Error processing person %s: %s" % ...)) calls _ after the loop bound it to an int, raising TypeError. Both fire only on the error paths, so they slipped through. The loop counter is unused; rename it to _batch_step so _ resolves to the module gettext everywhere. ruff (E9,F63,F7,F82) is clean on the module. This also clears the F82x lint error PR 820's ruff gate reports on the current tree. --- AssociationsTool/associationstool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssociationsTool/associationstool.py b/AssociationsTool/associationstool.py index ef9a5bed0..c9b8c8906 100644 --- a/AssociationsTool/associationstool.py +++ b/AssociationsTool/associationstool.py @@ -184,7 +184,7 @@ def _build_with_progress(self) -> bool: # Process next batch batch_size = 50 - for _ in range(batch_size): + for _batch_step in range(batch_size): if self._build_index >= len(self._plist): # Done processing self.stats_list = self._build_data From 2ad10b0e7cad37689b3a82787dbf5cfcaae51a85 Mon Sep 17 00:00:00 2001 From: Eric Lenerville Date: Mon, 20 Jul 2026 12:00:55 -0400 Subject: [PATCH 055/156] MediaVerify auto update media object names when they match old filename #12610 --- MediaVerify/MediaVerify.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MediaVerify/MediaVerify.py b/MediaVerify/MediaVerify.py index be567472b..2a55c0b18 100644 --- a/MediaVerify/MediaVerify.py +++ b/MediaVerify/MediaVerify.py @@ -390,6 +390,17 @@ def fix_media(self, button): for handle, new_path in self.moved_files: media = self.db.get_media_from_handle(handle) + + old_title = media.get_description() + old_path = media.get_path() + old_filename = os.path.splitext(os.path.basename(old_path))[0] + + # If the old media title matches the old filename then update the + # media title to match the new filename. + if old_title == old_filename: + new_title = os.path.splitext(os.path.basename(new_path))[0] + media.set_description(new_title) + media.set_path(new_path) self.db.commit_media(media, trans) From 6a56efe127ae8fc915aa93dbe195bb46410f7a47 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Fri, 24 Jul 2026 09:29:22 -0700 Subject: [PATCH 056/156] Merge MediaVerify auto update media object names #12610 #997 --- MediaVerify/MediaVerify.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaVerify/MediaVerify.gpr.py b/MediaVerify/MediaVerify.gpr.py index 9cae2fa3b..182a8b89f 100644 --- a/MediaVerify/MediaVerify.gpr.py +++ b/MediaVerify/MediaVerify.gpr.py @@ -30,7 +30,7 @@ id="mediaverify", name=_("Media Verify"), description=_("Verify that media is present in the correct path"), - version = '1.0.34', + version = '1.0.35', gramps_target_version="6.1", status=STABLE, fname="MediaVerify.py", From 5c6572c1e19098af1dd984592443136dabbf2920 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 07:59:29 -0700 Subject: [PATCH 057/156] Add AGENTS.md as a tool-agnostic mirror of CLAUDE.md Some agentic coding tools look for AGENTS.md rather than CLAUDE.md; keep the two in sync so contributors using either tool see the same repo conventions. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..735899371 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,260 @@ +# Agent Guidelines for gramps-project/addons-source + +This document specifies rules and conventions that agents should follow when +making changes to this repository. + +## Repository Overview + +`addons-source` holds the source code for third-party Gramps addons — +gramplets, views, tools, reports, importers/exporters, filter rules, database +backends, and other plugins that are not part of Gramps core. It is one of +three tightly-linked repositories: + +- **`gramps`** — Gramps core (the application these addons plug into). +- **`addons-source`** (this repo) — addon source code, developed here. +- **`addons`** — built `.addon.tgz` packages and listing JSON consumed by the + in-app Plugin Manager. Populated from this repo via `make.py`; not edited + by hand except by the maintainer doing a release build. + +There is no unified build or CI pipeline for the whole repo (the checked-in +`.travis.yml` is stale/unused) — each addon is largely independent. + +## Branch Model + +Branches are per Gramps release, e.g. `maintenance/gramps60`, +`maintenance/gramps61`. **Almost all addon work should target the maintenance +branch matching the current released Gramps version**, not `master`. Only use +`master` for an addon that depends on unreleased Gramps-core features. + +Pick the branch carefully: +- When creating a new addon or fixing an existing one, branch from + `origin/maintenance/gramps6X` (the current release line), not from a local + tracking branch that may carry unrelated WIP commits. +- When opening the PR, target that same branch on + `gramps-project/addons-source`. +- Before pushing, sync/rebase against upstream (`git pull --rebase`) so the + PR applies cleanly. + +All PRs must come from a personal fork, never from a branch pushed directly +to `gramps-project/addons-source` (see `CONTRIBUTING.md`, "Commit Your +Changes": "you want to put your changes into _your_ fork, _not_ the upstream +`gramps-project/addons-source` repository"). + +Weblate translation PRs are the one exception where commits must **not** be +squashed on merge; everything else can be merged/rebased normally. + +## Repository Layout + +Each addon is a single top-level directory, CamelCase-named to match its +Python import name (e.g. `FilterRules/`, `DataEntryGramplet/`). A directory +typically contains: + +- `AddonName.py` — the implementation. +- `AddonName.gpr.py` — the Gramps plugin registration file (required; see + below). +- `po/` — translation `.po` files, managed by `make.py`. +- `tests/` — optional unit tests (see Testing below). +- `MANIFEST` — optional; lists extra files (docs, data) to include in the + built package beyond the default `*.py`, `*.glade`, `*.xml`, `*.txt`, + `locale/*/LC_MESSAGES/*.mo`. +- `locale/` — generated `.mo` files; not something you hand-edit. + +Addon directories generally have **no `__init__.py`** (they rely on Python 3 +namespace packages) so that Gramps can add each one to `sys.path` at load +time and other addons can `import AddonName` directly. + +## Commands + +Addon packaging/translation tasks go through `make.py`, run from the repo +root. Point `GRAMPSPATH` at wherever *your* local Gramps checkout lives (it +defaults to `../../..`, i.e. it assumes `make.py` is being invoked from one +level inside a sibling-layout workspace — don't rely on that default, always +set it explicitly), and set `LANGUAGE=en_US.UTF-8`: + +```bash +GRAMPSPATH=/path/to/your/gramps LANGUAGE=en_US.UTF-8 python3 make.py gramps61 build AddonDirectory +``` + +Common subcommands (first positional arg is the branch/version tag, e.g. +`gramps61`): + +- `init AddonDirectory [lang]` — scaffold dirs / `.pot` for a new addon, or + an initial `po/-local.po`. +- `update AddonDirectory ` — refresh a language `.po` from the `.pot`. +- `compile [AddonDirectory|all]` — compile `.po` → `.mo`. +- `build [AddonDirectory|all]` — produce the downloadable `.addon.tgz`. +- `listing [AddonDirectory|all]` — generate/update the Plugin Manager + listing JSON. +- `clean AddonDirectory` — strip generated files (`locale/`, `*.pot`, etc.) + before committing. +- `manifest-check` — validate `MANIFEST` files. +- `as-needed` — build/list/clean only what's out of date, repo-wide. + +Unlike `GRAMPSPATH`, the `build`/`listing`/`check` subcommands write to a +**hardcoded relative path**, `../addons//...` — this isn't a +convention, it's how `make.py` itself resolves the output location, so it +only works if you've cloned the `addons` repo as a sibling of +`addons-source` (see `MAINTAINERS.md` for the recommended three-repo +workspace layout: `gramps` / `addons` / `addons-source` side by side). You +don't need that sibling checkout at all unless you're packaging/publishing +an addon. + +You do not need `make.py` just to write/test an addon's Python code — only +when packaging, translating, or updating the listing. + +## Testing + +Not every addon has tests, but when adding or fixing one, add regression +tests under `AddonName/tests/`, mirroring existing examples (`Form/tests/`, +`DataEntryGramplet/tests/`, `Sqlite/tests/`, `FilterRules/tests/`). + +Conventions here differ from Gramps core: + +- Test files are named `test_*.py` (pytest-style), **not** the `*_test.py` + suffix core Gramps uses — `unittest discover` here is invoked per-addon or + from the repo root without the core `-p "*_test.py"` pattern. +- Still use the `unittest` framework (`unittest.TestCase`), not `pytest` + APIs, even though filenames follow the `test_*.py` convention. +- `AddonName/tests/__init__.py` should be **empty**. Do not add + `gi.require_version(...)` pinning there or in individual test modules — + see GTK/GDK below. +- Because the addon directory has no `__init__.py`, test modules need a + `sys.path` hack to import it as a namespace package: + + ```python + ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + ``` + + then `from AddonName.addonmodule import SomeClass`. +- Build test data programmatically against a real (temp-dir) database rather + than relying on fixtures that don't exist in this repo: + + ```python + from gramps.gen.db.utils import make_database + from gramps.gen.db import DbTxn + + db = make_database("sqlite") + db.load(tempfile.mkdtemp(prefix="myaddon_")) + with DbTxn("build test db", db) as txn: + ... + ``` +- If a test needs `gramps.gen.filters.CustomFilters`, call + `reload_custom_filters()` **before** importing the `CustomFilters` name + from `gramps.gen.filters` — that function rebinds the module-level global + rather than mutating it in place, so importing the name first leaves you + with a stale `None`. + +### GTK / GDK version pinning + +The repo-root `tests/__init__.py` pins `gi.require_version("Gtk", "3.0")` and +`gi.require_version("Gdk", "3.0")` for the whole test suite (added in PR +#950). `gi.require_version` sets process-global state, so **individual addon +test files should not re-pin GTK/GDK** — that's now redundant. A new test +module that imports something pulling in `gramps.gui.*` (which happens at +import time for most GUI-facing addons) only needs a guard for hosts with no +PyGObject at all: + +```python +try: + import gi +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) +``` + +Do not add `gi.require_version(...)` calls back into new test files — if you +see them in an addon you're touching, they're safe to remove as redundant +(see the `Themes` addon's `tests/__init__.py` cleanup for precedent), but +don't remove them from files you aren't otherwise changing. + +### Running tests + +```bash +export GRAMPS_RESOURCES=/path/to/gramps/build/share # built Gramps checkout +export GDK_BACKEND=- +python3 -m unittest AddonName.tests.test_something -v +``` + +Run from the `addons-source` repo root so the addon's namespace package +resolves. + +## Code Style + +- Every new `.py` file needs the same GPL-2.0-or-later header with copyright + used throughout Gramps core. +- Group imports under comment-banner sections (`Standard Python modules`, + `GTK/Gnome modules`, `Gramps modules`), matching the style already used + across this repo (e.g. `FilterRules/isfamilyfiltermatchevent.py`). +- Format with [Black](https://black.readthedocs.io/) — not enforced by a + checked-in CI workflow here, but the existing codebase is Black-formatted + and new/changed files should be too (`black `). +- Docstrings: concise; full Sphinx `:param:`/`:returns:` style only when the + parameters aren't already obvious from the signature. + +## Internationalization + +Addons manage their own translations, separately from Gramps core. At the +top of an addon's main module: + +```python +from gramps.gen.const import GRAMPS_LOCALE as glocale + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.gettext +``` + +(`get_addon_translator` raises `ValueError` if the addon has no +`locale/` translations yet — fall back to the core translator rather than +letting the import crash.) Use `ngettext(singular, plural, n)` instead of a +single `_()` call for any string that counts a noun, for the same reason as +Gramps core: many languages have plural rules English doesn't, and only +`ngettext` lets gettext apply the target language's actual rule. + +## Plugin Registration (`.gpr.py`) + +Every addon needs an `AddonName.gpr.py` alongside `AddonName.py`: + +```python +register( + RULE, # or TOOL, GRAMPLET, REPORT, VIEW, GENERAL, IMPORT, EXPORT, ... + id="uniqueid", + name=_("Human-Readable Name"), + description=_("What it does."), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, # or UNSTABLE + fname="AddonName.py", + authors=["Author Name"], + authors_email=["author@example.com"], + help_url="Addon:WikiPageName", + ... # PTYPE-specific attributes, e.g. ruleclass/namespace for RULE +) +``` + +`gramps_target_version` and `version` are required. `help_url` should point +at the addon's Gramps wiki page (`Addon:PageName`), not a GitHub URL. + +> **Do not manually change `version` in a PR.** The `MAJOR.MINOR.PATCH` +> version in `.gpr.py` is bumped automatically by the `addons` repo's +> packaging build (`make.py`) when a change is released — the patch +> component increments on its own. Hand-editing it in a source PR just +> creates spurious diffs and can conflict with what the release build +> assigns; leave it as-is unless you're intentionally doing a MAJOR/MINOR +> bump for a breaking addon change. + +## Bug Reports and Commit Messages + +Bugs against addons are tracked in two places: the shared +[Gramps Mantis BT](https://gramps-project.org/bugs/view_all_bug_page.php) +and, informally, the [Gramps Discourse forum](https://gramps.discourse.group/) +— many addon issues start as a discourse thread rather than a Mantis report. +Unlike Gramps core, this repo has no automated changelog tooling parsing +commit messages, so there's no required `Fixes #N` keyword syntax — just +describe *why* the change was made, and link whatever report (Mantis bug or +discourse URL) motivated it if one exists. Don't invent a Mantis bug number +if you only have a discourse link or user report — link what you actually +have. From 3c7164f2e307623362a0bc9b69e4de6875b0302f Mon Sep 17 00:00:00 2001 From: Douglas Blank Date: Mon, 20 Jul 2026 10:12:53 -0400 Subject: [PATCH 058/156] Update AGENTS.md Co-authored-by: Eduard R. --- AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 735899371..f84d91c4d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,12 @@ This document specifies rules and conventions that agents should follow when making changes to this repository. +For the normative addon rules — MUST / SHOULD / MAY, with the origin of +each rule (upstream PR, maintainer ruling, Mantis id) cited inline — see +[docs/addon-development/16-guidelines.md](docs/addon-development/16-guidelines.md) +from the Addon Development manual (PR 994). This document covers the +hands-on agent workflow around those rules. + ## Repository Overview `addons-source` holds the source code for third-party Gramps addons — From d17a25a85f2e8feae34a2c8c7357616caf3c7029 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 20 Jul 2026 07:19:41 -0700 Subject: [PATCH 059/156] Address review feedback from Nick-Hall on PR #991 - Clarify branch model: "release" means major/feature release, not patch release; note master exists but isn't currently used for addon work. - Note LANGUAGE=en_US.UTF-8 is no longer required on Gramps v6.0+, and mention checking out the matching branch in the core checkout. - Soften Black formatting guidance to reflect it's not currently required. - Note that strings already translated in Gramps core are excluded from the Weblate Addons component. - Add maintainers/maintainers_email fields to the .gpr.py example. --- AGENTS.md | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f84d91c4d..ec9474780 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,10 +27,11 @@ There is no unified build or CI pipeline for the whole repo (the checked-in ## Branch Model -Branches are per Gramps release, e.g. `maintenance/gramps60`, -`maintenance/gramps61`. **Almost all addon work should target the maintenance -branch matching the current released Gramps version**, not `master`. Only use -`master` for an addon that depends on unreleased Gramps-core features. +Branches are per Gramps major or feature release, e.g. `maintenance/gramps60`, +`maintenance/gramps61` — not one branch per patch release. **Almost all addon +work should target the maintenance branch matching the current released +Gramps version.** A `master` branch exists but is not currently used for +addon work. Pick the branch carefully: - When creating a new addon or fixing an existing one, branch from @@ -75,10 +76,13 @@ Addon packaging/translation tasks go through `make.py`, run from the repo root. Point `GRAMPSPATH` at wherever *your* local Gramps checkout lives (it defaults to `../../..`, i.e. it assumes `make.py` is being invoked from one level inside a sibling-layout workspace — don't rely on that default, always -set it explicitly), and set `LANGUAGE=en_US.UTF-8`: +set it explicitly), and make sure that checkout has the matching branch +checked out too (e.g. `maintenance/gramps61` in core when building against +`gramps61` here). Setting `LANGUAGE=en_US.UTF-8` is no longer required on +Gramps v6.0 and later: ```bash -GRAMPSPATH=/path/to/your/gramps LANGUAGE=en_US.UTF-8 python3 make.py gramps61 build AddonDirectory +GRAMPSPATH=/path/to/your/gramps python3 make.py gramps61 build AddonDirectory ``` Common subcommands (first positional arg is the branch/version tag, e.g. @@ -192,15 +196,20 @@ resolves. - Group imports under comment-banner sections (`Standard Python modules`, `GTK/Gnome modules`, `Gramps modules`), matching the style already used across this repo (e.g. `FilterRules/isfamilyfiltermatchevent.py`). -- Format with [Black](https://black.readthedocs.io/) — not enforced by a - checked-in CI workflow here, but the existing codebase is Black-formatted - and new/changed files should be too (`black `). +- [Black](https://black.readthedocs.io/) formatting is not currently required + for addons — there's no checked-in CI workflow enforcing it here, though + that may change in the future. The existing codebase is largely + Black-formatted, so running `black ` on files you're already + touching is a reasonable default. - Docstrings: concise; full Sphinx `:param:`/`:returns:` style only when the parameters aren't already obvious from the signature. ## Internationalization -Addons manage their own translations, separately from Gramps core. At the +Addons manage their own translations, separately from Gramps core, though +both go through the same Weblate instance: strings already translated in +Gramps core are matched first, so those strings are excluded from the +Weblate _Addons_ component and don't need separate translation there. At the top of an addon's main module: ```python @@ -236,6 +245,8 @@ register( fname="AddonName.py", authors=["Author Name"], authors_email=["author@example.com"], + maintainers=["Maintainer Name"], + maintainers_email=["maintainer@example.com"], help_url="Addon:WikiPageName", ... # PTYPE-specific attributes, e.g. ruleclass/namespace for RULE ) @@ -243,6 +254,9 @@ register( `gramps_target_version` and `version` are required. `help_url` should point at the addon's Gramps wiki page (`Addon:PageName`), not a GitHub URL. +`maintainers`/`maintainers_email` identify who currently maintains the +addon and may differ from `authors`/`authors_email` (the original author) +once upkeep passes to someone else. > **Do not manually change `version` in a PR.** The `MAJOR.MINOR.PATCH` > version in `.gpr.py` is bumped automatically by the `addons` repo's From 2dc7050c735131d852e48559dcd4a436b255f4b2 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 26 Jul 2026 08:42:17 -0700 Subject: [PATCH 060/156] Add Windows Development section addressing MSYS2 vs native Windows Per hgohel's suggestion on PR #991 (comment 5026237564) and follow-up discussion, clarify the distinction between MSYS2 (POSIX-like shell, safe to run make.py/tests in) and native Windows cmd/PowerShell (unverified tooling) so agents know what they can and can't run. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index ec9474780..46170c611 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -189,6 +189,36 @@ python3 -m unittest AddonName.tests.test_something -v Run from the `addons-source` repo root so the addon's namespace package resolves. +## Windows Development + +Per `CONTRIBUTING.md`, addons here are developed and tested primarily on +Linux; Windows and macOS are possible but far less thoroughly reviewed. +"Windows" itself covers two different things agents should not conflate: + +- **MSYS2** — the POSIX-like shell/toolchain environment Gramps's own + Windows installer is built with. A shell running inside MSYS2 behaves like + a Linux shell (`bash`, POSIX paths) and can run this repo's `make.py` and + `unittest` invocations normally. +- **Native Windows** — a bare `cmd.exe` or PowerShell host with no MSYS2 or + WSL layer underneath. The commands and paths used elsewhere in this + document (`make.py`, `python3 -m unittest ...`, the `GRAMPSPATH` / + `GRAMPS_RESOURCES` env vars) assume a POSIX shell and are not verified + against native `cmd`/PowerShell. + +If you are running as an agent on a native Windows host: + +- Reading, writing, and refactoring addon Python code is fine — the code + itself is portable. +- Do not run this repo's tests or `make.py` directly in `cmd`/PowerShell and + report the result as a real pass/fail — the tooling isn't validated there, + so a failure may be environmental noise rather than an actual regression. +- Ask the user to run the same command inside an MSYS2 shell, WSL, or a + Linux container/VM and share the output, rather than guessing at the + result yourself. + +If you're already inside MSYS2 or WSL, treat it as a POSIX shell and follow +the rest of this document (Commands, Testing) as written. + ## Code Style - Every new `.py` file needs the same GPL-2.0-or-later header with copyright From 04077f3e43022a85842870049bbe3e1c75a178e6 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Fri, 17 Jul 2026 07:50:57 -0700 Subject: [PATCH 061/156] PersonRelationshipFilter: fix HandleError crash and mismatched name fields IsSiblingofNamedSibling crashed with HandleError whenever applied to a person with no recorded parents, because get_family_from_handle(None) raises rather than returning None. RegExpPersonal/RegExpFamily also searched mismatched Name fields (title was listed twice in the personal list, and call name was searched by the family/surname rule instead), so personal search never matched a person's call name and surname search could false-positive on an unrelated title or call name. Adds unit tests covering both regressions plus the general relationship-matching rules, and brings the addon up to date with black/mypy. Co-Authored-By: Claude Sonnet 5 --- .../PersonRelationshipFilter.gpr.py | 38 ++ .../PersonRelationshipFilter.py | 353 ++++++++++++++++++ PersonRelationshipFilter/tests/__init__.py | 0 .../test_person_relationship_filter_rules.py | 326 ++++++++++++++++ 4 files changed, 717 insertions(+) create mode 100644 PersonRelationshipFilter/PersonRelationshipFilter.gpr.py create mode 100644 PersonRelationshipFilter/PersonRelationshipFilter.py create mode 100644 PersonRelationshipFilter/tests/__init__.py create mode 100644 PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py diff --git a/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py new file mode 100644 index 000000000..0805f6f67 --- /dev/null +++ b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py @@ -0,0 +1,38 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2015 Nick Hall +# Copyright (C) 2024 Paul Womack (BugBear) +# Copyright (C) 2026 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +register( + GRAMPLET, + id="Person Relationship Filter", + name=_("Person Relationship Filter"), + description=_("Gramplet providing a person filter on relationships"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="PersonRelationshipFilter.py", + height=200, + gramplet="PersonRelationshipFilter", + gramplet_title=_("Relationship Filter"), + navtypes=["Person"], + authors=["Paul Womack", "Doug Blank"], + authors_email=["doug.blank@gmail.com"], + help_url="Addon:AdvancedPersonFilter", +) diff --git a/PersonRelationshipFilter/PersonRelationshipFilter.py b/PersonRelationshipFilter/PersonRelationshipFilter.py new file mode 100644 index 000000000..a35169405 --- /dev/null +++ b/PersonRelationshipFilter/PersonRelationshipFilter.py @@ -0,0 +1,353 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2010 Doug Blank +# Copyright (C) 2011 Nick Hall +# Copyright (C) 2011 Tim G L Lyons +# Copyright (C) 2024 Paul Womack (BugBear) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- + +from gi.repository import Gtk + +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.plug import Gramplet + +from gramps.gen.filters.rules import Rule +from gramps.gen.filters.rules.person import ProbablyAlive +from gramps.gen.lib.person import Person +from gramps.gen.lib import Date +from gramps.gen.datehandler import displayer +from gramps.gen.filters import GenericFilter +from gramps.gui import widgets +from gramps.gui.filters.sidebar import SidebarFilter + +_ = glocale.translation.gettext + + +class _RegExpNameList(Rule): + """Rule that checks for full or partial name matches""" + + labels = [_("Text:")] + name = _("People with a name matching ") + description = _( + "Matches people's names containing a substring or " + "matching a regular expression" + ) + category = _("General filters") + allow_regex = True + + def field_list(self, name): + raise NotImplementedError + + def apply_to_one(self, db, person): + for name in [person.primary_name] + person.alternate_names: + for field in self.field_list(name): + if self.match_substring(0, field): + return True + else: + return False + + +class RegExpPersonal(_RegExpNameList): + def field_list(self, name): + return [name.first_name, name.title, name.call, name.nick] + + +class RegExpFamily(_RegExpNameList): + def field_list(self, name): + return [name.get_surname(), name.suffix, name.famnick] + + +class _HasNamedRelation(Rule): + labels = [_("Filter name:")] + name = _("Children of name match") + category = _("Family filters") + description = _("Matches children of anybody with a given name") + + def __init__(self, arg, name_matcher, use_regex=False): + super().__init__(arg, use_regex) + self.name_matcher = name_matcher(arg, use_regex) + + def prepare(self, db, user): + self.name_matcher.requestprepare(db, user) + + def reset(self): + self.name_matcher.requestreset() + + def get_rel_list(self, db, person): + raise NotImplementedError + + def get_spouse_list(self, db, person): + handles = [] + for fam_id in person.family_list: + fam = db.get_raw_family_data(fam_id) + if fam: + for spouse_id in [fam.father_handle, fam.mother_handle]: + if not spouse_id: + continue + if spouse_id == person.handle: + continue + handles.append(spouse_id) + return handles + + def apply_to_one(self, db, person): + for rel_id in self.get_rel_list(db, person): + if rel_id: + rel = db.get_raw_person_data(rel_id) + if self.name_matcher.apply_to_one(db, rel): + return True + return False + + +class _HasNamedParent(_HasNamedRelation): + def get_parent_families(self, db, person): + families = [] + for fam_id in person.parent_family_list: + fam = db.get_family_from_handle(fam_id) + if fam: + families.append(fam) + return families + + +class HasNamedFather(_HasNamedParent): + def get_rel_list(self, db, person): + return map( + lambda fam: fam.get_father_handle(), self.get_parent_families(db, person) + ) + + +class HasNamedMother(_HasNamedParent): + def get_rel_list(self, db, person): + return map( + lambda fam: fam.get_mother_handle(), self.get_parent_families(db, person) + ) + + +class IsSiblingofNamedSibling(_HasNamedRelation): + def get_rel_list(self, db, person): + handles = [] + fam_id = person.get_main_parents_family_handle() # or all families, per above? + fam = db.get_family_from_handle(fam_id) if fam_id else None + if fam: + for child_ref in fam.get_child_ref_list(): + if child_ref and child_ref.ref != person.handle: + handles.append(child_ref.ref) + return handles + + +class HasNamedChild(_HasNamedRelation): + def get_rel_list(self, db, person): + handles = [] + for fam_id in person.family_list: + fam = db.get_family_from_handle(fam_id) + if fam: + for child_ref in fam.get_child_ref_list(): + if child_ref: + handles.append(child_ref.ref) + return handles + + +class HasNamedSpouse(_HasNamedRelation): + def get_rel_list(self, db, person): + return self.get_spouse_list(db, person) + + +class HasName(_HasNamedRelation): + def get_rel_list(self, db, person): + if person.gender == Person.FEMALE and isinstance( + self.name_matcher, RegExpFamily + ): + # for female surnames, we want to trawl the spouses surnames + handles = self.get_spouse_list(db, person) + handles.append(person.handle) + return handles + else: + return [person.handle] + + +def extract_text(entry_widget): + """ + Extract the text from the entry widget, strips off any extra spaces. + """ + return str(entry_widget.get_text().strip()) + + +# leverage to split the name into fore and aft +class SearchableNamePair: + def __init__(self, label, rule_class): + self.widget_personal = widgets.BasicEntry() + self.widget_personal.set_placeholder_text(_("given")) + self.widget_family = widgets.BasicEntry() + self.widget_family.set_placeholder_text(_("surname")) + self.label = label + self.rule_class = rule_class + + def place(self, sidebar): + # container.add_text_entry(self.label, self.widget_personal) + # self.add_text_entry(container, self.label, self.widget_personal) + # unrolled + + sidebar.grid.attach(widgets.BasicLabel(self.label), 1, sidebar.position, 1, 1) + + self.widget_personal.set_hexpand(True) + sidebar.grid.attach(self.widget_personal, 2, sidebar.position, 1, 1) + self.widget_personal.connect("key-press-event", sidebar.key_press) + + self.widget_family.set_hexpand(True) + sidebar.grid.attach(self.widget_family, 3, sidebar.position, 1, 1) + self.widget_family.connect("key-press-event", sidebar.key_press) + sidebar.position += 1 + + def clear(self): + self.widget_personal.set_text("") + self.widget_family.set_text("") + + def _add_to_filter(self, generic_filter, regex, widget, search_class): + v = extract_text(widget) + if v: + rule = self.rule_class([v], search_class, use_regex=regex) + generic_filter.add_rule(rule) + + def add_to_filter(self, generic_filter, regex): + self._add_to_filter(generic_filter, regex, self.widget_personal, RegExpPersonal) + self._add_to_filter(generic_filter, regex, self.widget_family, RegExpFamily) + + +# ------------------------------------------------------------------------- +# +# PersonSidebarFilter class +# +# ------------------------------------------------------------------------- +class PersonSidebarFilter(SidebarFilter): + + def __init__(self, dbstate, uistate, clicked): + self.clicked_func = clicked + self.sensitive_regex = False + + self.names = [ + SearchableNamePair(_("Person"), HasName), + SearchableNamePair(_("Father"), HasNamedFather), + SearchableNamePair(_("Mother"), HasNamedMother), + SearchableNamePair(_("Spouse"), HasNamedSpouse), + SearchableNamePair(_("Sibling 1"), IsSiblingofNamedSibling), + SearchableNamePair(_("Sibling 2"), IsSiblingofNamedSibling), + SearchableNamePair(_("Child 1"), HasNamedChild), + SearchableNamePair(_("Child 2"), HasNamedChild), + ] + self.filter_alive = widgets.DateEntry(uistate, []) + + self.filter_regex = Gtk.CheckButton(label=_("Use regular expressions")) + + SidebarFilter.__init__(self, dbstate, uistate, "Person") + + def create_widget(self): + exdate1 = Date() + exdate2 = Date() + exdate1.set( + Date.QUAL_NONE, + Date.MOD_RANGE, + Date.CAL_GREGORIAN, + (0, 0, 1800, False, 0, 0, 1900, False), + ) + exdate2.set( + Date.QUAL_NONE, Date.MOD_BEFORE, Date.CAL_GREGORIAN, (0, 0, 1850, False) + ) + + msg1 = displayer.display(exdate1) + msg2 = displayer.display(exdate2) + + for w in self.names: + w.place(self) + + self.add_text_entry( + _("Probably Alive"), + self.filter_alive, + _("example: '%(msg1)s' or '%(msg2)s'") % {"msg1": msg1, "msg2": msg2}, + ) + self.add_regex_entry(self.filter_regex) + + def clear(self, obj): + for w in self.names: + w.clear() + self.filter_alive.set_text("") + + def get_filter(self): + """ + Extracts the text strings from the sidebar, and uses them to build up + a new filter. + """ + + regex = self.filter_regex.get_active() + + # build a GenericFilter + generic_filter = GenericFilter() + for w in self.names: + w.add_to_filter(generic_filter, regex) + + alive = extract_text(self.filter_alive) + if alive: + rule = ProbablyAlive([alive]) + generic_filter.add_rule(rule) + + return generic_filter + + +# ------------------------------------------------------------------------- +# +# Filter class +# +# ------------------------------------------------------------------------- +class Filter(Gramplet): + """ + The base class for all filter gramplets. + """ + + FILTER_CLASS: type[SidebarFilter] | None = None + + def init(self): + self.filter = self.FILTER_CLASS( + self.dbstate, self.uistate, self.__filter_clicked + ) + self.widget = self.filter.get_widget() + self.gui.get_container_widget().remove(self.gui.textview) + self.gui.get_container_widget().add(self.widget) + self.widget.show_all() + + def __filter_clicked(self): + """ + Called when the filter apply button is clicked. + """ + self.gui.view.generic_filter = self.filter.get_filter() + self.gui.view.build_tree() + + +# ------------------------------------------------------------------------- +# +# PersonFilter class +# +# ------------------------------------------------------------------------- +class PersonRelationshipFilter(Filter): + """ + A gramplet providing a Person Filter. + """ + + FILTER_CLASS = PersonSidebarFilter diff --git a/PersonRelationshipFilter/tests/__init__.py b/PersonRelationshipFilter/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py new file mode 100644 index 000000000..cbbe68eb3 --- /dev/null +++ b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py @@ -0,0 +1,326 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Doug Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Unit tests for the relationship-matching filter rules defined in +``PersonRelationshipFilter.py``. + +The tests build a small family tree directly in an in-memory database +(no dependency on the shared ``example.gramps`` fixture) so each +rule's relationship traversal and name-field matching can be checked +precisely. Two regressions are covered explicitly: + +* ``IsSiblingofNamedSibling`` used to raise ``HandleError`` when + applied to a person with no recorded parents, because + ``get_family_from_handle(None)`` raises rather than returning + ``None``. +* ``RegExpPersonal``/``RegExpFamily`` searched mismatched ``Name`` + fields (``title`` was listed twice in the personal field list, and + ``call`` name was searched by the family/surname rule instead), so + personal search never matched a person's call name and surname + search could false-positive on an unrelated title or call name. +""" + +# ------------------------ +# Python modules +# ------------------------ +import os +import sys +import unittest + +# The addon imports Gtk at module load — skip cleanly if gi/Gtk are not +# available, mirroring what other addons' test suites do. +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Gdk", "3.0") +except (ImportError, ValueError, AttributeError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +# ------------------------ +# Gramps modules +# ------------------------ +# The addon directory goes on sys.path so ``import PersonRelationshipFilter`` +# resolves the flat ``PersonRelationshipFilter.py`` module directly (there is +# no wrapping package — the file lives right in the addon directory). +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + import gramps +except ImportError as err: + raise unittest.SkipTest("gramps package not available: %s" % err) + +if "GRAMPS_RESOURCES" not in os.environ: + os.environ["GRAMPS_RESOURCES"] = os.path.dirname(os.path.dirname(gramps.__file__)) + +try: + from gramps.gen.db import DbTxn + from gramps.gen.db.utils import make_database + from gramps.gen.lib import ChildRef, Family, Name, Person, Surname + + from PersonRelationshipFilter import ( + HasName, + HasNamedChild, + HasNamedFather, + HasNamedMother, + HasNamedSpouse, + IsSiblingofNamedSibling, + RegExpFamily, + RegExpPersonal, + ) +except Exception as err: # noqa: BLE001 — environment guard + raise unittest.SkipTest("PersonRelationshipFilter module unavailable: %s" % err) + + +def _make_name(first, surname, call="", nick="", title="", famnick=""): + """Build a Name with the given given/surname plus the secondary fields + under test (call name, nickname, title, family nickname).""" + name = Name() + name.set_first_name(first) + name.set_call_name(call) + name.set_nick_name(nick) + name.set_title(title) + name.famnick = famnick + surname_obj = Surname() + surname_obj.set_surname(surname) + name.set_surname_list([surname_obj]) + return name + + +def _make_database(): + db = make_database("sqlite") + db.load(":memory:") + return db + + +class PersonRelationshipFilterRulesTest(unittest.TestCase): + """ + Exercises the rules against a small, precisely-built family tree:: + + Frank Farnsworth (father) + Martha Miller (mother) + -> Carol Farnsworth (call "Caz", nick "Care", title "Dr.", + family nickname "Farns") + -> Dan Farnsworth + Carol Farnsworth + Sam Smith (spouse) + Owen Orphanage -- no recorded parents + """ + + @classmethod + def setUpClass(cls): + cls.db = _make_database() + with DbTxn("build test tree", cls.db) as trans: + father = Person() + father.set_gender(Person.MALE) + father.set_primary_name(_make_name("Frank", "Farnsworth")) + father_handle = cls.db.add_person(father, trans) + + mother = Person() + mother.set_gender(Person.FEMALE) + mother.set_primary_name(_make_name("Martha", "Miller")) + mother_handle = cls.db.add_person(mother, trans) + + carol = Person() + carol.set_gender(Person.FEMALE) + carol.set_primary_name( + _make_name( + "Carol", + "Farnsworth", + call="Caz", + nick="Care", + title="Dr.", + famnick="Farns", + ) + ) + carol_handle = cls.db.add_person(carol, trans) + + dan = Person() + dan.set_gender(Person.MALE) + dan.set_primary_name(_make_name("Dan", "Farnsworth")) + dan_handle = cls.db.add_person(dan, trans) + + orphan = Person() + orphan.set_gender(Person.MALE) + orphan.set_primary_name(_make_name("Owen", "Orphanage")) + orphan_handle = cls.db.add_person(orphan, trans) + + spouse = Person() + spouse.set_gender(Person.MALE) + spouse.set_primary_name(_make_name("Sam", "Smith")) + spouse_handle = cls.db.add_person(spouse, trans) + + parent_family = Family() + parent_family.set_father_handle(father_handle) + parent_family.set_mother_handle(mother_handle) + for child_handle in (carol_handle, dan_handle): + child_ref = ChildRef() + child_ref.set_reference_handle(child_handle) + parent_family.add_child_ref(child_ref) + parent_family_handle = cls.db.add_family(parent_family, trans) + + father.add_family_handle(parent_family_handle) + mother.add_family_handle(parent_family_handle) + carol.add_parent_family_handle(parent_family_handle) + dan.add_parent_family_handle(parent_family_handle) + + marriage = Family() + marriage.set_father_handle(spouse_handle) + marriage.set_mother_handle(carol_handle) + marriage_handle = cls.db.add_family(marriage, trans) + spouse.add_family_handle(marriage_handle) + carol.add_family_handle(marriage_handle) + + for person in (father, mother, carol, dan, orphan, spouse): + cls.db.commit_person(person, trans) + + cls.father = cls.db.get_person_from_handle(father_handle) + cls.mother = cls.db.get_person_from_handle(mother_handle) + cls.carol = cls.db.get_person_from_handle(carol_handle) + cls.dan = cls.db.get_person_from_handle(dan_handle) + cls.orphan = cls.db.get_person_from_handle(orphan_handle) + cls.spouse = cls.db.get_person_from_handle(spouse_handle) + + @classmethod + def tearDownClass(cls): + cls.db.close() + cls.db = None + + def _match_name(self, matcher_class, value, person, use_regex=False): + """Apply a bare RegExpPersonal/RegExpFamily rule to one person.""" + rule = matcher_class([value], use_regex=use_regex) + rule.requestprepare(self.db, None) + try: + return rule.apply_to_one(self.db, person) + finally: + rule.requestreset() + + def _match_relation(self, rule_class, value, person, matcher_class=RegExpPersonal): + """Apply a _HasNamedRelation rule (Father/Mother/Sibling/Child/Spouse) + to one person.""" + rule = rule_class([value], matcher_class, use_regex=False) + rule.requestprepare(self.db, None) + try: + return rule.apply_to_one(self.db, person) + finally: + rule.requestreset() + + # -- name field matching -------------------------------------------- + + def test_personal_matches_first_name(self): + self.assertTrue(self._match_name(RegExpPersonal, "Carol", self.carol)) + + def test_personal_matches_call_name(self): + """Regression: the personal field list used to list 'title' twice + instead of including the call name.""" + self.assertTrue(self._match_name(RegExpPersonal, "Caz", self.carol)) + + def test_personal_matches_nick_name(self): + self.assertTrue(self._match_name(RegExpPersonal, "Care", self.carol)) + + def test_personal_matches_title(self): + self.assertTrue(self._match_name(RegExpPersonal, "Dr", self.carol)) + + def test_personal_does_not_match_surname(self): + self.assertFalse(self._match_name(RegExpPersonal, "Farnsworth", self.carol)) + + def test_family_matches_surname(self): + self.assertTrue(self._match_name(RegExpFamily, "Farnsworth", self.carol)) + + def test_family_matches_famnick(self): + self.assertTrue(self._match_name(RegExpFamily, "Farns", self.carol)) + + def test_family_does_not_match_title(self): + """Regression: the family/surname field list used to include the + personal title field, causing false-positive surname matches.""" + self.assertFalse(self._match_name(RegExpFamily, "Dr", self.carol)) + + def test_family_does_not_match_call_name(self): + """Regression: the family/surname field list used to include the + personal call name field, causing false-positive surname matches.""" + self.assertFalse(self._match_name(RegExpFamily, "Caz", self.carol)) + + def test_regex_mode_matches_pattern(self): + self.assertTrue( + self._match_name(RegExpPersonal, "^Car", self.carol, use_regex=True) + ) + self.assertFalse( + self._match_name(RegExpPersonal, "^ar", self.carol, use_regex=True) + ) + + # -- relationship traversal ------------------------------------------ + + def test_has_named_father(self): + self.assertTrue(self._match_relation(HasNamedFather, "Frank", self.carol)) + self.assertFalse(self._match_relation(HasNamedFather, "Nobody", self.carol)) + + def test_has_named_mother(self): + self.assertTrue(self._match_relation(HasNamedMother, "Martha", self.carol)) + + def test_has_named_child(self): + self.assertTrue(self._match_relation(HasNamedChild, "Carol", self.father)) + self.assertTrue(self._match_relation(HasNamedChild, "Dan", self.mother)) + + def test_has_named_spouse(self): + self.assertTrue(self._match_relation(HasNamedSpouse, "Sam", self.carol)) + self.assertFalse(self._match_relation(HasNamedSpouse, "Frank", self.carol)) + + def test_is_sibling_of_named_sibling(self): + self.assertTrue( + self._match_relation(IsSiblingofNamedSibling, "Dan", self.carol) + ) + self.assertTrue( + self._match_relation(IsSiblingofNamedSibling, "Carol", self.dan) + ) + + def test_is_sibling_of_named_sibling_excludes_self(self): + self.assertFalse( + self._match_relation(IsSiblingofNamedSibling, "Carol", self.carol) + ) + + def test_is_sibling_of_named_sibling_with_no_parents_does_not_crash(self): + """Regression: applying this rule to a person with no recorded + parents used to raise HandleError from + get_family_from_handle(None).""" + self.assertFalse( + self._match_relation(IsSiblingofNamedSibling, "Anyone", self.orphan) + ) + + def test_has_name_matches_own_name(self): + self.assertTrue(self._match_relation(HasName, "Carol", self.carol)) + + def test_has_name_female_family_search_trawls_spouse_surname(self): + """A female's own family/surname search also matches her spouse's + surname (so 'Person' search finds her under her married name).""" + self.assertTrue( + self._match_relation( + HasName, "Smith", self.carol, matcher_class=RegExpFamily + ) + ) + self.assertFalse( + self._match_relation( + HasName, "Smith", self.father, matcher_class=RegExpFamily + ) + ) + + +if __name__ == "__main__": + unittest.main() From 06881f151f2634a8510982d9daf0fcb073d75c35 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Fri, 17 Jul 2026 13:24:48 -0700 Subject: [PATCH 062/156] PersonRelationshipFilter: fix help_url to reference this addon's own wiki page help_url pointed at Addon:AdvancedPersonFilter, a different addon name left over from this addon's origin, instead of Addon:PersonRelationshipFilter. Co-Authored-By: Claude Sonnet 5 --- PersonRelationshipFilter/PersonRelationshipFilter.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py index 0805f6f67..90381f9d8 100644 --- a/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py +++ b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py @@ -34,5 +34,5 @@ navtypes=["Person"], authors=["Paul Womack", "Doug Blank"], authors_email=["doug.blank@gmail.com"], - help_url="Addon:AdvancedPersonFilter", + help_url="Addon:PersonRelationshipFilter", ) From 0f1623a4ff8d47c93c1e7d517149a31730eaef47 Mon Sep 17 00:00:00 2001 From: Douglas Blank Date: Thu, 23 Jul 2026 13:53:10 -0400 Subject: [PATCH 063/156] Apply suggestion from @dsblank --- .../tests/test_person_relationship_filter_rules.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py index cbbe68eb3..68317e3bf 100644 --- a/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py +++ b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py @@ -45,16 +45,6 @@ import sys import unittest -# The addon imports Gtk at module load — skip cleanly if gi/Gtk are not -# available, mirroring what other addons' test suites do. -try: - import gi - - gi.require_version("Gtk", "3.0") - gi.require_version("Gdk", "3.0") -except (ImportError, ValueError, AttributeError) as err: - raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) - # ------------------------ # Gramps modules # ------------------------ From 76539a05dbd66e889e5f0fb0fcf5646d9116ded5 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 26 Jul 2026 08:50:43 -0700 Subject: [PATCH 064/156] Fix ImportError in PersonRelationshipFilter test module import `from PersonRelationshipFilter import (HasName, ...)` breaks when unittest loads this file as `PersonRelationshipFilter.tests.test_...`, because by then the outer `PersonRelationshipFilter` is already a namespace package in sys.modules, so the bare import looks for each name as an attribute of the package instead of importing the PersonRelationshipFilter.py submodule that defines them. Use the fully-qualified `PersonRelationshipFilter.PersonRelationshipFilter` import path, matching the pattern already used in DataEntryGramplet/tests/test_data_entry_gramplet.py. Reported by GaryGriffin in gramps-project/addons-source#987. Co-Authored-By: Claude Sonnet 5 --- .../tests/test_person_relationship_filter_rules.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py index 68317e3bf..0b4ccb67f 100644 --- a/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py +++ b/PersonRelationshipFilter/tests/test_person_relationship_filter_rules.py @@ -48,9 +48,14 @@ # ------------------------ # Gramps modules # ------------------------ -# The addon directory goes on sys.path so ``import PersonRelationshipFilter`` -# resolves the flat ``PersonRelationshipFilter.py`` module directly (there is -# no wrapping package — the file lives right in the addon directory). +# Addon root goes on sys.path so ``from PersonRelationshipFilter. +# PersonRelationshipFilter import ...`` resolves the class/functions inside +# the addon module. The fully-qualified form matters: when unittest loads +# this file as ``PersonRelationshipFilter.tests.test_...``, the outer +# ``PersonRelationshipFilter`` is already a namespace package in +# ``sys.modules``, so a bare ``from PersonRelationshipFilter import X`` +# would look for ``X`` as an attribute of that namespace package instead of +# importing the ``PersonRelationshipFilter.py`` submodule that defines it. ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if ADDON_DIR not in sys.path: sys.path.insert(0, ADDON_DIR) @@ -68,7 +73,7 @@ from gramps.gen.db.utils import make_database from gramps.gen.lib import ChildRef, Family, Name, Person, Surname - from PersonRelationshipFilter import ( + from PersonRelationshipFilter.PersonRelationshipFilter import ( HasName, HasNamedChild, HasNamedFather, From d8c0f4f1a4ed76cf0eab87e2503abf9ccad6c66c Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sun, 26 Jul 2026 11:08:49 -0700 Subject: [PATCH 065/156] Merge PersonRelationshipFilter#987 --- .../PersonRelationshipFilter.gpr.py | 2 +- PersonRelationshipFilter/po/template.pot | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 PersonRelationshipFilter/po/template.pot diff --git a/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py index 90381f9d8..bed0adf9c 100644 --- a/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py +++ b/PersonRelationshipFilter/PersonRelationshipFilter.gpr.py @@ -24,7 +24,7 @@ id="Person Relationship Filter", name=_("Person Relationship Filter"), description=_("Gramplet providing a person filter on relationships"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="PersonRelationshipFilter.py", diff --git a/PersonRelationshipFilter/po/template.pot b/PersonRelationshipFilter/po/template.pot new file mode 100644 index 000000000..77083244b --- /dev/null +++ b/PersonRelationshipFilter/po/template.pot @@ -0,0 +1,117 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-26 11:05-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:49 +msgid "Text:" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:50 +msgid "People with a name matching " +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:52 +msgid "" +"Matches people's names containing a substring or matching a regular " +"expression" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:55 +msgid "General filters" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:81 +msgid "Filter name:" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:83 +msgid "Family filters" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:198 +msgid "given" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:200 +msgid "surname" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:247 +msgid "Person" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:248 +msgid "Father" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:249 +msgid "Mother" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:250 +msgid "Spouse" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:258 +msgid "Use regular expressions" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" From 8a32cbc19daedd0eba08da9b8487938aadbca003 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 19 Jul 2026 09:42:17 -0700 Subject: [PATCH 066/156] Add WordClouds gramplet Adds three word-cloud gramplets (Given Name, Surname, Place) that render names/places as a size-weighted, click-to-navigate word cloud instead of the old plain-text link list, using a new Cairo/Pango WordCloudWidget with a spiral placement algorithm. Includes a slider+entry option widget (SliderOption/GuiSliderOption) registered via BasePluginManager.register_option so no changes to Gramps core are required. Based on ideas from ClmntPnd's gramps core PR https://github.com/gramps-project/gramps/pull/2223, adapted here as a self-contained addon so it works with unmodified stable Gramps releases. Given Name/Surname/Place Word Cloud use new ids and class names distinct from the built-in Given Name Cloud / Surname Cloud gramplets to avoid any plugin registration collision. --- WordClouds/WordClouds.gpr.py | 67 +++ WordClouds/cloudgramplet.py | 233 ++++++++++ WordClouds/givennamewordcloudgramplet.py | 73 ++++ WordClouds/placewordcloudgramplet.py | 80 ++++ WordClouds/slideroption.py | 168 ++++++++ WordClouds/surnamewordcloudgramplet.py | 75 ++++ WordClouds/tests/__init__.py | 0 WordClouds/tests/test_cloudgramplet_logic.py | 166 ++++++++ WordClouds/tests/test_imports.py | 80 ++++ WordClouds/tests/test_slideroption.py | 86 ++++ WordClouds/tests/test_wordcloudwidget.py | 88 ++++ WordClouds/wordcloudwidget.py | 424 +++++++++++++++++++ 12 files changed, 1540 insertions(+) create mode 100644 WordClouds/WordClouds.gpr.py create mode 100644 WordClouds/cloudgramplet.py create mode 100644 WordClouds/givennamewordcloudgramplet.py create mode 100644 WordClouds/placewordcloudgramplet.py create mode 100644 WordClouds/slideroption.py create mode 100644 WordClouds/surnamewordcloudgramplet.py create mode 100644 WordClouds/tests/__init__.py create mode 100644 WordClouds/tests/test_cloudgramplet_logic.py create mode 100644 WordClouds/tests/test_imports.py create mode 100644 WordClouds/tests/test_slideroption.py create mode 100644 WordClouds/tests/test_wordcloudwidget.py create mode 100644 WordClouds/wordcloudwidget.py diff --git a/WordClouds/WordClouds.gpr.py b/WordClouds/WordClouds.gpr.py new file mode 100644 index 000000000..ebe25bf93 --- /dev/null +++ b/WordClouds/WordClouds.gpr.py @@ -0,0 +1,67 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +register( + GRAMPLET, + id="Given Name Word Cloud", + name=_("Given Name Word Cloud"), + description=_("Gramplet showing all given names as a word cloud"), + status=STABLE, + version="1.0.0", + fname="givennamewordcloudgramplet.py", + height=300, + expand=True, + gramplet="GivenNameWordCloudGramplet", + gramplet_title=_("Given Name Word Cloud"), + gramps_target_version="6.1", + help_url="WordClouds", +) + +register( + GRAMPLET, + id="Surname Word Cloud", + name=_("Surname Word Cloud"), + description=_("Gramplet showing all surnames as a word cloud"), + status=STABLE, + version="1.0.0", + fname="surnamewordcloudgramplet.py", + height=300, + expand=True, + gramplet="SurnameWordCloudGramplet", + gramplet_title=_("Surname Word Cloud"), + gramps_target_version="6.1", + help_url="WordClouds", +) + +register( + GRAMPLET, + id="Place Word Cloud", + name=_("Place Word Cloud"), + description=_("Gramplet showing all places as a word cloud"), + status=STABLE, + version="1.0.0", + fname="placewordcloudgramplet.py", + height=300, + expand=True, + gramplet="PlaceWordCloudGramplet", + gramplet_title=_("Place Word Cloud"), + gramps_target_version="6.1", + help_url="WordClouds", +) diff --git a/WordClouds/cloudgramplet.py b/WordClouds/cloudgramplet.py new file mode 100644 index 000000000..28b71cb8e --- /dev/null +++ b/WordClouds/cloudgramplet.py @@ -0,0 +1,233 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . + +# ------------------------------------------------------------------------ +# +# Python modules +# +# ------------------------------------------------------------------------ +from abc import abstractmethod + +# ------------------------------------------------------------------------ +# +# Gramps modules +# +# ------------------------------------------------------------------------ +from gramps.gen.plug import Gramplet, BasePluginManager +from gramps.gen.config import config +from gramps.gen.const import GRAMPS_LOCALE as glocale + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.sgettext + +from wordcloudwidget import WordCloudWidget +from slideroption import SliderOption, GuiSliderOption + +# ------------------------------------------------------------------------ +# +# Constants +# +# ------------------------------------------------------------------------ + +_YIELD_INTERVAL = 350 + +_DEFAULT_COLOR_LOW = "#99ccff" # (0.6, 0.8, 1.0) +_DEFAULT_COLOR_HIGH = "#003399" # (0.0, 0.2, 0.6) +_DEFAULT_COLOR_HOVER = "#cc0000" # (0.8, 0.0, 0.0) +_DEFAULT_QUALITY = 0.0 + + +def _hex_to_rgb(hex_color): + h = hex_color.lstrip("#") + return tuple(int(h[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) + + +# ------------------------------------------------------------------------ +# +# CloudGramplet class +# +# ------------------------------------------------------------------------ +class CloudGramplet(Gramplet): + """A gramplet that displays a word cloud where word size reflects frequency.""" + + def init(self): + pmgr = BasePluginManager.get_instance() + pmgr.register_option(SliderOption, GuiSliderOption) + + self.top_size = 150 + self.color_low = _DEFAULT_COLOR_LOW + self.color_high = _DEFAULT_COLOR_HIGH + self.color_hover = _DEFAULT_COLOR_HOVER + self.quality = _DEFAULT_QUALITY + self.filter_missing = True + self.value_name = "default_value_name" + self.preference_no_value = "" + self._values_linked_data = {} + + self.word_cloud = WordCloudWidget( + [], + on_click=self._on_word_clicked, + color_low=_hex_to_rgb(self.color_low), + color_high=_hex_to_rgb(self.color_high), + color_hover=_hex_to_rgb(self.color_hover), + quality=self.quality, + ) + self.gui.get_container_widget().remove(self.gui.textview) + self.gui.get_container_widget().add(self.word_cloud) + self.word_cloud.show() + + def set_value_name(self, value_name): + """What the cloud displays. For a name cloud, `value_name` is "name".""" + self.value_name = _(value_name) + + def set_preference_no_value(self, preference_no_value): + """Config key holding the default text to show when there are no values.""" + self.preference_no_value = preference_no_value + + def _on_word_clicked(self, word): + linked_data = self._values_linked_data.get(word) + if linked_data is not None: + self.on_item_clicked(word, linked_data) + + def on_item_clicked(self, word, linked_data): + """Called when the user clicks a word. Subclasses override to navigate.""" + + @abstractmethod + def db_changed(self): + """Connect the cloud with the database. + See the example in surnamewordcloudgramplet.py. + """ + + @abstractmethod + def get_items(self) -> list: + """How to access data in the cloud. Must return an iterator of + (value, linked_data, count) triples. + See the example in surnamewordcloudgramplet.py. + """ + + def on_load(self): + data = self.gui.data + if len(data) >= 1: + self.top_size = int(data[0]) + if len(data) >= 5: + self.color_low = data[1] + self.color_high = data[2] + self.color_hover = data[3] + self.quality = float(data[4]) + if len(data) >= 6: + self.filter_missing = bool(int(data[5])) + self.word_cloud.set_colors( + _hex_to_rgb(self.color_low), + _hex_to_rgb(self.color_high), + _hex_to_rgb(self.color_hover), + ) + self.word_cloud.set_quality(self.quality) + + def _read_options(self): + self.top_size = int( + self.get_option(_("Number of %s") % self.value_name).get_value() + ) + self.color_low = self.get_option(_("Color (low)")).get_value() + self.color_high = self.get_option(_("Color (high)")).get_value() + self.color_hover = self.get_option(_("Hover color")).get_value() + self.quality = float(self.get_option(_("Layout quality")).get_value()) + self.filter_missing = self.get_option( + _("Filter missing/unknown words") + ).get_value() + + def save_update_options(self, widget=None): + self._read_options() + self.gui.data = [ + self.top_size, + self.color_low, + self.color_high, + self.color_hover, + self.quality, + int(self.filter_missing), + ] + self.update() + + def save_options(self): + self._read_options() + + def main(self): + yield True + + yield_counter = 0 + + values_counts = {} + values_linked_data = {} + + for value, linked_data, count in self.get_items(): + if value not in values_counts: + values_linked_data[value] = linked_data + values_counts[value] = count + else: + values_counts[value] += count + + yield_counter += 1 + if not yield_counter % _YIELD_INTERVAL: + yield True + + # count order: [(value, count), ...] + sorted_values = sorted( + list(values_counts.items()), key=(lambda k: k[1]), reverse=True + ) + + # limit to top_size distinct values + selected_values = sorted_values[: self.top_size] + + # Build words list for the widget, resolving empty-value display text + self._values_linked_data = {} + words = [] + for value, count in selected_values: + if len(value) == 0: + if self.preference_no_value != "": + display = config.get(self.preference_no_value) + else: + display = _("[Missing %s]") % self.value_name + else: + display = value + self._values_linked_data[display] = values_linked_data.get(value) + words.append((display, count)) + + self.word_cloud.configure( + quality=self.quality, + color_low=_hex_to_rgb(self.color_low), + color_high=_hex_to_rgb(self.color_high), + color_hover=_hex_to_rgb(self.color_hover), + ) + self.word_cloud.set_words(words) + + def build_options(self): + from gramps.gen.plug.menu import BooleanOption, ColorOption + + self.add_option( + SliderOption(_("Number of %s") % self.value_name, self.top_size, 1, 150) + ) + self.add_option(ColorOption(_("Color (low)"), self.color_low)) + self.add_option(ColorOption(_("Color (high)"), self.color_high)) + self.add_option(ColorOption(_("Hover color"), self.color_hover)) + self.add_option(SliderOption(_("Layout quality"), self.quality, 0.0, 1.0, 0.1)) + self.add_option( + BooleanOption(_("Filter missing/unknown words"), self.filter_missing) + ) diff --git a/WordClouds/givennamewordcloudgramplet.py b/WordClouds/givennamewordcloudgramplet.py new file mode 100644 index 000000000..14fa4b2e1 --- /dev/null +++ b/WordClouds/givennamewordcloudgramplet.py @@ -0,0 +1,73 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2009 Pander Musubi +# Copyright (C) 2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gui.plug.quick import run_quick_report_by_name + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.sgettext + +from cloudgramplet import CloudGramplet + + +# ------------------------------------------------------------------------- +# +# GivenNameWordCloudGramplet class +# +# ------------------------------------------------------------------------- +class GivenNameWordCloudGramplet(CloudGramplet): + """Implementation of a Cloud gramplet for given names.""" + + def init(self): + CloudGramplet.init(self) + self.set_value_name("given name") + self.set_preference_no_value("preferences.no-given-text") + self.set_tooltip(_("Click given name to view people with that given name")) + + def on_item_clicked(self, word, linked_data): + run_quick_report_by_name( + self.dbstate, self.uistate, "samegivens_misc", linked_data + ) + + def db_changed(self): + self.connect(self.dbstate.db, "person-add", self.update) + self.connect(self.dbstate.db, "person-delete", self.update) + self.connect(self.dbstate.db, "person-update", self.update) + self.connect(self.dbstate.db, "person-rebuild", self.update) + self.connect(self.dbstate.db, "family-rebuild", self.update) + + def get_items(self): + counts = {} + for person in self.dbstate.db.iter_people(): + allnames = [person.get_primary_name()] + person.get_alternate_names() + for name in allnames: + given_name = name.get_first_name().strip() + if self.filter_missing and (not given_name or given_name == "?"): + continue + counts[given_name] = counts.get(given_name, 0) + 1 + return [(given_name, given_name, counts[given_name]) for given_name in counts] diff --git a/WordClouds/placewordcloudgramplet.py b/WordClouds/placewordcloudgramplet.py new file mode 100644 index 000000000..d762b4d43 --- /dev/null +++ b/WordClouds/placewordcloudgramplet.py @@ -0,0 +1,80 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2009 Pander Musubi +# Copyright (C) 2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.display.place import displayer as place_displayer +from gramps.gui.plug.quick import run_quick_report_by_name + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.sgettext + +from cloudgramplet import CloudGramplet + + +# ------------------------------------------------------------------------- +# +# PlaceWordCloudGramplet class +# +# ------------------------------------------------------------------------- +class PlaceWordCloudGramplet(CloudGramplet): + """Implementation of a Cloud gramplet for place names. + + Word size reflects how many times each place is referenced in the database. + """ + + def init(self): + CloudGramplet.init(self) + self.set_value_name("place name") + self.set_tooltip(_("Click place name to view references")) + + def on_item_clicked(self, word, linked_data): + run_quick_report_by_name( + self.dbstate, self.uistate, "placereferences", linked_data + ) + + def db_changed(self): + self.connect(self.dbstate.db, "place-add", self.update) + self.connect(self.dbstate.db, "place-delete", self.update) + self.connect(self.dbstate.db, "place-update", self.update) + self.connect(self.dbstate.db, "event-add", self.update) + self.connect(self.dbstate.db, "event-update", self.update) + self.connect(self.dbstate.db, "event-delete", self.update) + + def get_items(self) -> list: + # Use the full hierarchical name so each place maps to a unique string, + # and count by backlinks so word size reflects how often it is used. + items = [] + for place in self.dbstate.db.iter_places(): + handle = place.handle + count = len(list(self.dbstate.db.find_backlink_handles(handle))) + if count > 0: + placename = place_displayer.display(self.dbstate.db, place) + if self.filter_missing and placename in (None, "", "?"): + continue + items.append((placename, handle, count)) + return items diff --git a/WordClouds/slideroption.py b/WordClouds/slideroption.py new file mode 100644 index 000000000..0fc98074a --- /dev/null +++ b/WordClouds/slideroption.py @@ -0,0 +1,168 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . +# +""" +A NumberOption rendered as a horizontal slider with a text entry, and the +matching GTK widget. Registered with the plugin manager as an external +option so it does not require any change to Gramps core. +""" + +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import math + +# ------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +# ------------------------------------------------------------------------- +from gi.repository import Gtk + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.plug.menu import NumberOption + + +# ------------------------------------------------------------------------- +# +# SliderOption class +# +# ------------------------------------------------------------------------- +class SliderOption(NumberOption): + """ + A NumberOption rendered as a horizontal slider + text entry widget. + Saves only on mouse-up or entry commit, not on every drag tick. + All min/max/step/value logic is inherited from NumberOption. + """ + + +# ------------------------------------------------------------------------- +# +# GuiSliderOption class +# +# ------------------------------------------------------------------------- +class GuiSliderOption(Gtk.Box): + """ + Displays a number option as a horizontal slider alongside a text entry. + The option value is only committed on mouse-up or entry activation, + not on every drag tick. + """ + + def __init__(self, option, dbstate, uistate, track, override): + self.__option = option + + step = self.__option.get_step() + self.__decimals = 0 + if step < 1: + self.__decimals = int(math.log10(step) * -1) + + Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=4) + + adj = Gtk.Adjustment( + value=self.__option.get_value(), + lower=self.__option.get_min(), + upper=self.__option.get_max(), + step_increment=step, + ) + self.__scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=adj) + self.__scale.set_digits(self.__decimals) + self.__scale.set_hexpand(True) + self.__scale.set_draw_value(False) + + self.__entry = Gtk.Entry() + self.__entry.set_width_chars(6) + self.__entry.set_max_width_chars(8) + self.__entry.set_text(self.__format(self.__option.get_value())) + + self.pack_start(self.__scale, True, True, 0) + self.pack_start(self.__entry, False, False, 0) + + # Live sync: update entry text while dragging, but do not commit yet. + self.scalekey = self.__scale.connect("value-changed", self.__scale_moved) + # Commit on mouse-up only. + self.__scale.connect("button-release-event", self.__scale_released) + # Commit entry on Enter or focus-out. + self.entrykey = self.__entry.connect("activate", self.__entry_activated) + self.__entry.connect("focus-out-event", self.__entry_activated) + + # Programmatic option change -> update both widgets. + self.valuekey = self.__option.connect("value-changed", self.__value_changed) + self.conkey = self.__option.connect("avail-changed", self.__update_avail) + self.__update_avail() + + self.set_tooltip_text(self.__option.get_help()) + + def __format(self, value): + if self.__decimals == 0: + return str(int(value)) + return "{:.{}f}".format(value, self.__decimals) + + def __scale_moved(self, obj): + """Update entry text live during drag without committing to the option.""" + self.__entry.handler_block(self.entrykey) + self.__entry.set_text(self.__format(self.__scale.get_value())) + self.__entry.handler_unblock(self.entrykey) + + def __scale_released(self, obj, event): + """Commit the slider value to the option on mouse-up.""" + vtype = type(self.__option.get_value()) + self.__scale.handler_block(self.scalekey) + self.__option.set_value(vtype(self.__scale.get_value())) + self.__scale.handler_unblock(self.scalekey) + + def __entry_activated(self, obj, event=None): + """Commit a typed value from the entry to the option.""" + try: + vtype = type(self.__option.get_value()) + value = vtype(float(self.__entry.get_text())) + value = max(self.__option.get_min(), min(self.__option.get_max(), value)) + except (ValueError, TypeError): + self.__entry.set_text(self.__format(self.__option.get_value())) + return + if value == self.__option.get_value(): + return + self.__scale.handler_block(self.scalekey) + self.__scale.set_value(value) + self.__scale.handler_unblock(self.scalekey) + self.__option.set_value(value) + + def __value_changed(self): + """Handle a programmatic change to the option value.""" + value = self.__option.get_value() + self.__scale.handler_block(self.scalekey) + self.__entry.handler_block(self.entrykey) + self.__scale.set_value(value) + self.__entry.set_text(self.__format(value)) + self.__entry.handler_unblock(self.entrykey) + self.__scale.handler_unblock(self.scalekey) + + def __update_avail(self): + avail = self.__option.get_available() + self.set_sensitive(avail) + + def clean_up(self): + self.__option.disconnect(self.valuekey) + self.__option.disconnect(self.conkey) + self.__option = None diff --git a/WordClouds/surnamewordcloudgramplet.py b/WordClouds/surnamewordcloudgramplet.py new file mode 100644 index 000000000..858c53553 --- /dev/null +++ b/WordClouds/surnamewordcloudgramplet.py @@ -0,0 +1,75 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gui.plug.quick import run_quick_report_by_name + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.sgettext + +from cloudgramplet import CloudGramplet + + +# ------------------------------------------------------------------------- +# +# SurnameWordCloudGramplet class +# +# ------------------------------------------------------------------------- +class SurnameWordCloudGramplet(CloudGramplet): + """Implementation of a Cloud gramplet for surnames.""" + + def init(self): + CloudGramplet.init(self) + self.set_value_name("surname") + self.set_preference_no_value("preferences.no-surname-text") + self.set_tooltip(_("Click surname to view people with that surname")) + + def on_item_clicked(self, word, linked_data): + run_quick_report_by_name( + self.dbstate, self.uistate, "samesurnames", linked_data + ) + + def db_changed(self): + self.connect(self.dbstate.db, "person-add", self.update) + self.connect(self.dbstate.db, "person-delete", self.update) + self.connect(self.dbstate.db, "person-update", self.update) + self.connect(self.dbstate.db, "person-rebuild", self.update) + self.connect(self.dbstate.db, "family-rebuild", self.update) + + def get_items(self): + counts = {} + handles = {} + for person in self.dbstate.db.iter_people(): + allnames = [person.get_primary_name()] + person.get_alternate_names() + for name in allnames: + surname = name.get_surname().strip() + if self.filter_missing and (not surname or surname == "?"): + continue + counts[surname] = counts.get(surname, 0) + 1 + if surname not in handles: + handles[surname] = person.handle + return [(surname, handles[surname], counts[surname]) for surname in counts] diff --git a/WordClouds/tests/__init__.py b/WordClouds/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/WordClouds/tests/test_cloudgramplet_logic.py b/WordClouds/tests/test_cloudgramplet_logic.py new file mode 100644 index 000000000..87dd66216 --- /dev/null +++ b/WordClouds/tests/test_cloudgramplet_logic.py @@ -0,0 +1,166 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Functional tests for each gramplet's get_items() against a real in-memory +Gramps database, bypassing Gramplet.__init__ (and so all GTK/GUI setup) +since get_items() only touches self.dbstate and self.filter_missing. +""" + +import os +import sys +import types +import unittest + +try: + import gi + + gi.require_version("Gtk", "3.0") +except (ImportError, ValueError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import make_database +from gramps.gen.lib import Event, EventType, Name, Place, PlaceName, Person, Surname + +from cloudgramplet import _hex_to_rgb +from givennamewordcloudgramplet import GivenNameWordCloudGramplet +from placewordcloudgramplet import PlaceWordCloudGramplet +from surnamewordcloudgramplet import SurnameWordCloudGramplet + + +def _make_gramplet(cls, db, filter_missing): + """Build a gramplet instance without running Gramplet.__init__/init(), + which would require a live GUI. get_items() only needs dbstate/filter_missing. + """ + gramplet = object.__new__(cls) + gramplet.dbstate = types.SimpleNamespace(db=db) + gramplet.filter_missing = filter_missing + return gramplet + + +class CloudGrampletLogicTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.db = make_database("sqlite") + cls.db.load(":memory:") + + with DbTxn("Add test objects", cls.db) as trans: + cls.smith1 = cls._add_person(cls.db, trans, "John", "Smith") + cls.smith2 = cls._add_person(cls.db, trans, "Jane", "Smith") + cls.doe = cls._add_person(cls.db, trans, "John", "Doe") + cls.blank = cls._add_person(cls.db, trans, "", "") + + cls.place = cls._add_place(cls.db, trans, "Springfield") + cls.unused_place = cls._add_place(cls.db, trans, "Shelbyville") + + event = Event() + event.set_type(EventType(EventType.BIRTH)) + event.set_place_handle(cls.place.handle) + cls.db.add_event(event, trans) + + @classmethod + def tearDownClass(cls): + cls.db.close() + + @staticmethod + def _add_person(db, trans, given, surname): + person = Person() + name = Name() + name.set_first_name(given) + gramps_surname = Surname() + gramps_surname.set_surname(surname) + name.set_surname_list([gramps_surname]) + person.set_primary_name(name) + db.add_person(person, trans) + return person + + @staticmethod + def _add_place(db, trans, place_name): + place = Place() + place.set_name(PlaceName(value=place_name)) + db.add_place(place, trans) + return place + + +class TestGivenNameWordCloudGramplet(CloudGrampletLogicTest): + def test_counts_given_names(self): + gramplet = _make_gramplet( + GivenNameWordCloudGramplet, self.db, filter_missing=True + ) + items = dict((value, count) for value, _linked, count in gramplet.get_items()) + self.assertEqual(items["John"], 2) + self.assertEqual(items["Jane"], 1) + self.assertNotIn("", items) + + def test_filter_missing_false_includes_blank(self): + gramplet = _make_gramplet( + GivenNameWordCloudGramplet, self.db, filter_missing=False + ) + items = dict((value, count) for value, _linked, count in gramplet.get_items()) + self.assertIn("", items) + + +class TestSurnameWordCloudGramplet(CloudGrampletLogicTest): + def test_counts_surnames_and_links_a_handle(self): + gramplet = _make_gramplet( + SurnameWordCloudGramplet, self.db, filter_missing=True + ) + items = { + value: (linked, count) for value, linked, count in gramplet.get_items() + } + self.assertEqual(items["Smith"][1], 2) + self.assertEqual(items["Doe"][1], 1) + self.assertIn(items["Smith"][0], (self.smith1.handle, self.smith2.handle)) + self.assertNotIn("", items) + + def test_filter_missing_false_includes_blank(self): + gramplet = _make_gramplet( + SurnameWordCloudGramplet, self.db, filter_missing=False + ) + items = dict((value, count) for value, _linked, count in gramplet.get_items()) + self.assertIn("", items) + + +class TestPlaceWordCloudGramplet(CloudGrampletLogicTest): + def test_only_referenced_places_are_included(self): + gramplet = _make_gramplet(PlaceWordCloudGramplet, self.db, filter_missing=True) + items = { + value: (linked, count) for value, linked, count in gramplet.get_items() + } + self.assertIn("Springfield", items) + self.assertEqual(items["Springfield"][0], self.place.handle) + self.assertEqual(items["Springfield"][1], 1) + # Shelbyville has no backlinks, so it must not appear. + self.assertNotIn("Shelbyville", items) + + +class TestHexToRgb(unittest.TestCase): + def test_black(self): + self.assertEqual(_hex_to_rgb("#000000"), (0.0, 0.0, 0.0)) + + def test_white(self): + self.assertEqual(_hex_to_rgb("#ffffff"), (1.0, 1.0, 1.0)) + + +if __name__ == "__main__": + unittest.main() diff --git a/WordClouds/tests/test_imports.py b/WordClouds/tests/test_imports.py new file mode 100644 index 000000000..7740b9839 --- /dev/null +++ b/WordClouds/tests/test_imports.py @@ -0,0 +1,80 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Regression test: every WordClouds module must import cleanly and expose +the class named in WordClouds.gpr.py, and each gramplet class must be a +Gramplet subclass. +""" + +import os +import sys +import unittest + +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Gdk", "3.0") +except (ImportError, ValueError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +# Make sure addon modules are importable from the parent directory. +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +class TestWordCloudsImports(unittest.TestCase): + """Every module registered in WordClouds.gpr.py must import cleanly.""" + + def test_wordcloudwidget_imports(self): + import wordcloudwidget + + self.assertTrue(hasattr(wordcloudwidget, "WordCloudWidget")) + + def test_slideroption_imports(self): + import slideroption + + self.assertTrue(hasattr(slideroption, "SliderOption")) + self.assertTrue(hasattr(slideroption, "GuiSliderOption")) + + def test_cloudgramplet_imports(self): + import cloudgramplet + + self.assertTrue(hasattr(cloudgramplet, "CloudGramplet")) + + def test_gramplet_classes_are_gramplet_subclasses(self): + from gramps.gen.plug import Gramplet + + from givennamewordcloudgramplet import GivenNameWordCloudGramplet + from surnamewordcloudgramplet import SurnameWordCloudGramplet + from placewordcloudgramplet import PlaceWordCloudGramplet + + for cls in ( + GivenNameWordCloudGramplet, + SurnameWordCloudGramplet, + PlaceWordCloudGramplet, + ): + self.assertTrue( + issubclass(cls, Gramplet), "%s must be a Gramplet subclass" % cls + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/WordClouds/tests/test_slideroption.py b/WordClouds/tests/test_slideroption.py new file mode 100644 index 000000000..2d004fce2 --- /dev/null +++ b/WordClouds/tests/test_slideroption.py @@ -0,0 +1,86 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Tests for SliderOption / GuiSliderOption: the vendored NumberOption +subclass and its GTK widget, registered with the plugin manager as an +external option so WordClouds needs no Gramps core changes. +""" + +import os +import sys +import unittest + +try: + import gi + + gi.require_version("Gtk", "3.0") +except (ImportError, ValueError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +from gi.repository import Gtk + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from slideroption import GuiSliderOption, SliderOption + + +class TestSliderOption(unittest.TestCase): + """SliderOption inherits all min/max/step/value logic from NumberOption.""" + + def test_initial_value(self): + option = SliderOption("Quality", 0.5, 0.0, 1.0, 0.1) + self.assertEqual(option.get_value(), 0.5) + self.assertEqual(option.get_min(), 0.0) + self.assertEqual(option.get_max(), 1.0) + self.assertEqual(option.get_step(), 0.1) + + def test_set_value(self): + option = SliderOption("Count", 10, 1, 150) + option.set_value(75) + self.assertEqual(option.get_value(), 75) + + +class TestGuiSliderOption(unittest.TestCase): + """Smoke tests for the GTK widget wrapping a SliderOption.""" + + def _make_widget(self, option): + return GuiSliderOption(option, None, None, [], False) + + def test_widget_reflects_initial_value(self): + option = SliderOption("Count", 10, 1, 150) + widget = self._make_widget(option) + self.assertIsInstance(widget, Gtk.Box) + + def test_option_value_change_updates_widget_without_error(self): + option = SliderOption("Count", 10, 1, 150) + self._make_widget(option) + # Should not raise: exercises __value_changed via the signal. + option.set_value(42) + self.assertEqual(option.get_value(), 42) + + def test_clean_up_disconnects_without_error(self): + option = SliderOption("Count", 10, 1, 150) + widget = self._make_widget(option) + widget.clean_up() + + +if __name__ == "__main__": + unittest.main() diff --git a/WordClouds/tests/test_wordcloudwidget.py b/WordClouds/tests/test_wordcloudwidget.py new file mode 100644 index 000000000..687c7a25d --- /dev/null +++ b/WordClouds/tests/test_wordcloudwidget.py @@ -0,0 +1,88 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Tests for the pure layout-math helpers in wordcloudwidget.py: font-size and +color interpolation, and axis-aligned bounding-box overlap detection. +""" + +import os +import sys +import unittest + +try: + import gi + + gi.require_version("Gtk", "3.0") + gi.require_version("Pango", "1.0") + gi.require_version("PangoCairo", "1.0") +except (ImportError, ValueError) as err: + raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err) + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from wordcloudwidget import _aabbs_overlap, _count_to_color, _count_to_fontsize + + +class TestCountToFontsize(unittest.TestCase): + def test_min_and_max_count_map_to_min_and_max_font(self): + self.assertAlmostEqual(_count_to_fontsize(1, 1, 100, 8, 20), 8) + self.assertAlmostEqual(_count_to_fontsize(100, 1, 100, 8, 20), 20) + + def test_equal_min_and_max_count_returns_midpoint(self): + self.assertAlmostEqual(_count_to_fontsize(5, 5, 5, 8, 20), 14) + + def test_higher_count_never_yields_smaller_font(self): + low = _count_to_fontsize(2, 1, 100, 8, 20) + high = _count_to_fontsize(50, 1, 100, 8, 20) + self.assertLessEqual(low, high) + + +class TestCountToColor(unittest.TestCase): + def test_min_count_is_low_color(self): + color = _count_to_color(1, 1, 100, (0.0, 0.0, 0.0), (1.0, 1.0, 1.0)) + self.assertEqual(color, (0.0, 0.0, 0.0)) + + def test_max_count_is_high_color(self): + color = _count_to_color(100, 1, 100, (0.0, 0.0, 0.0), (1.0, 1.0, 1.0)) + self.assertEqual(color, (1.0, 1.0, 1.0)) + + def test_equal_min_and_max_count_returns_midpoint_color(self): + color = _count_to_color(5, 5, 5, (0.0, 0.0, 0.0), (1.0, 1.0, 1.0)) + self.assertEqual(color, (0.5, 0.5, 0.5)) + + +class TestAabbsOverlap(unittest.TestCase): + def test_identical_boxes_overlap(self): + self.assertTrue(_aabbs_overlap(0, 0, 10, 10, 0, 0, 10, 10)) + + def test_disjoint_boxes_do_not_overlap(self): + self.assertFalse(_aabbs_overlap(0, 0, 10, 10, 20, 20, 10, 10)) + + def test_edge_touching_boxes_do_not_overlap(self): + # Box B starts exactly where box A ends: touching, not overlapping. + self.assertFalse(_aabbs_overlap(0, 0, 10, 10, 10, 0, 10, 10)) + + def test_partial_overlap_is_detected(self): + self.assertTrue(_aabbs_overlap(0, 0, 10, 10, 5, 5, 10, 10)) + + +if __name__ == "__main__": + unittest.main() diff --git a/WordClouds/wordcloudwidget.py b/WordClouds/wordcloudwidget.py new file mode 100644 index 000000000..de001c960 --- /dev/null +++ b/WordClouds/wordcloudwidget.py @@ -0,0 +1,424 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-2009 Douglas S. Blank +# Copyright (C) 2026 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . +# +""" +Provides a GTK word cloud widget. +""" + +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import math +import random +from functools import lru_cache + +# ------------------------------------------------------------------------- +# +# GTK/Cairo/Pango modules +# +# ------------------------------------------------------------------------- +import cairo +import gi + +gi.require_version("Gtk", "3.0") +gi.require_version("Pango", "1.0") +gi.require_version("PangoCairo", "1.0") +from gi.repository import Gtk, Gdk, GLib, Pango, PangoCairo + +# ------------------------------------------------------------------------- +# +# Constants +# +# ------------------------------------------------------------------------- +_SPIRAL_STEP = 2.5 +_MAX_THETA = 50 * math.pi +_MAX_SHRINK_STEPS = 3 + +_QUALITY_LEVELS = [ + (10, 72, 4), + (8, 64, 3), + (7, 56, 2), + (6, 48, 1), +] + + +# ------------------------------------------------------------------------- +# +# Helper functions +# +# ------------------------------------------------------------------------- +def _search_params(quality): + q = max(0.0, min(1.0, quality)) + theta_step = 2.0 * (0.025**q) + n_fallbacks = round(q * 2) + return theta_step, n_fallbacks + + +def _count_to_fontsize(count, min_c, max_c, min_font, max_font): + if min_c == max_c: + return (min_font + max_font) / 2 + count = max(count, 1) + min_c = max(min_c, 1) + max_c = max(max_c, 1) + t = (math.log(count) - math.log(min_c)) / (math.log(max_c) - math.log(min_c)) + t = max(0.0, min(1.0, t)) + return min_font + t * (max_font - min_font) + + +def _count_to_color(count, min_c, max_c, color_low, color_high): + if min_c == max_c: + t = 0.5 + else: + min_c = max(min_c, 1) + max_c = max(max_c, 1) + t = (math.log(max(count, 1)) - math.log(min_c)) / ( + math.log(max_c) - math.log(min_c) + ) + t = max(0.0, min(1.0, t)) + r = color_low[0] + t * (color_high[0] - color_low[0]) + g = color_low[1] + t * (color_high[1] - color_low[1]) + b = color_low[2] + t * (color_high[2] - color_low[2]) + return (r, g, b) + + +def _aabbs_overlap(ax, ay, aw, ah, bx, by, bw, bh): + return not (ax + aw <= bx or bx + bw <= ax or ay + ah <= by or by + bh <= ay) + + +def _spiral_positions(cx, cy, theta_step, max_theta, theta_offset=0.0): + theta = theta_offset + while theta - theta_offset < max_theta: + r = _SPIRAL_STEP * theta + yield (cx + r * math.cos(theta), cy + r * math.sin(theta)) + theta += theta_step + + +def _make_font_desc(font_size, style=Pango.Style.NORMAL): + desc = Pango.FontDescription() + desc.set_family("Sans") + desc.set_weight(Pango.Weight.BOLD) + desc.set_style(style) + desc.set_absolute_size(font_size * Pango.SCALE) + return desc + + +@lru_cache(maxsize=None) +def _measure_word(word, font_size): + surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) + ctx = cairo.Context(surf) + layout = PangoCairo.create_layout(ctx) + layout.set_font_description(_make_font_desc(font_size)) + layout.set_text(word, -1) + tw, th = layout.get_pixel_size() + return tw, th + + +def _try_place(word, font_size, cx, cy, vertical, placed, canvas_w, canvas_h, padding): + tw, th = _measure_word(word, font_size) + if vertical: + aw = th + padding * 2 + ah = tw + padding * 2 + else: + aw = tw + padding * 2 + ah = th + padding * 2 + + ax = cx - aw / 2 + ay = cy - ah / 2 + + if ax < 0 or ay < 0 or ax + aw > canvas_w or ay + ah > canvas_h: + return None + + for p in placed: + if _aabbs_overlap(ax, ay, aw, ah, p["ax"], p["ay"], p["aw"], p["ah"]): + return None + + return { + "word": word, + "font_size": font_size, + "ax": ax, + "ay": ay, + "aw": aw, + "ah": ah, + "vertical": vertical, + "tw": tw, + "th": th, + "padding": padding, + } + + +def _place_word(word, font_size, canvas_w, canvas_h, placed, padding, quality=1.0): + theta_step, n_fallbacks = _search_params(quality) + + cx0, cy0 = canvas_w / 2, canvas_h / 2 + jx = random.uniform(-canvas_w / 6, canvas_w / 6) + jy = random.uniform(-canvas_h / 6, canvas_h / 6) + cx, cy = cx0 + jx, cy0 + jy + + orientations = [False, True] if random.random() < 0.5 else [True, False] + + for shrink in range(_MAX_SHRINK_STEPS + 1): + fs = font_size * (0.9**shrink) + for px, py in _spiral_positions(cx, cy, theta_step, _MAX_THETA): + for vertical in orientations: + result = _try_place( + word, fs, px, py, vertical, placed, canvas_w, canvas_h, padding + ) + if result is not None: + return result + fallback_offsets = [math.pi / 3, 2 * math.pi / 3] + for theta_offset in fallback_offsets[:n_fallbacks]: + for px, py in _spiral_positions( + cx0, cy0, theta_step, _MAX_THETA, theta_offset + ): + for vertical in orientations: + result = _try_place( + word, + fs, + px, + py, + vertical, + placed, + canvas_w, + canvas_h, + padding, + ) + if result is not None: + return result + + return None + + +# ------------------------------------------------------------------------- +# +# WordCloudWidget class +# +# ------------------------------------------------------------------------- +class WordCloudWidget(Gtk.DrawingArea): + """ + A GTK DrawingArea that renders a word cloud. + + words : list of (word: str, count: int) + on_click : callable(word: str) or None + color_low : RGB tuple (0-1) for the lowest count + color_high : RGB tuple (0-1) for the highest count + color_hover : RGB tuple (0-1) drawn when the mouse is over a word + quality : 0-1; 1 = tightest packing (slow), 0 = greedy (fast) + """ + + def __init__( + self, + words, + on_click=None, + color_low=(0.6, 0.8, 1.0), + color_high=(0.0, 0.2, 0.6), + color_hover=(0.8, 0.0, 0.0), + quality=0.0, + ): + super().__init__() + self._words = words + self._on_click = on_click + self._color_low = color_low + self._color_high = color_high + self._color_hover = color_hover + self._quality = max(0.0, min(1.0, quality)) + self._layout = [] + self._layout_size = (0, 0) + self._hovered = None + self._resize_timer = None + self._computing = False + self._compute_id = None + + self.add_events( + Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK + ) + self.connect("draw", self._on_draw) + self.connect("size-allocate", self._on_size_allocate) + self.connect("button-press-event", self._on_click_event) + self.connect("motion-notify-event", self._on_motion) + + def set_words(self, words): + self._words = words + self._invalidate() + + def set_quality(self, quality): + self._quality = max(0.0, min(1.0, quality)) + self._invalidate() + + def set_colors(self, color_low, color_high, color_hover): + self._color_low = color_low + self._color_high = color_high + self._color_hover = color_hover + self.queue_draw() + + def configure( + self, quality=None, color_low=None, color_high=None, color_hover=None + ): + """Update settings without triggering a redraw; call set_words() after.""" + if quality is not None: + self._quality = max(0.0, min(1.0, quality)) + if color_low is not None: + self._color_low = color_low + if color_high is not None: + self._color_high = color_high + if color_hover is not None: + self._color_hover = color_hover + + def _invalidate(self): + self._layout_size = (0, 0) + self._computing = True + if self._compute_id is not None: + GLib.source_remove(self._compute_id) + self._compute_id = None + self.queue_draw() + + def _compute_layout(self, canvas_w, canvas_h): + self._layout = [] + if not self._words: + return + + counts = [max(c, 1) for _, c in self._words] + min_c, max_c = min(counts), max(counts) + + best_placed = [] + for min_font, max_font, padding in _QUALITY_LEVELS: + word_info = [] + for (word, count), c in zip(self._words, counts): + fs = _count_to_fontsize(c, min_c, max_c, min_font, max_font) + color = _count_to_color( + c, min_c, max_c, self._color_low, self._color_high + ) + word_info.append((word, c, fs, color)) + word_info.sort(key=lambda x: x[2], reverse=True) + + placed = [] + for word, count, fs, color in word_info: + result = _place_word( + word, fs, canvas_w, canvas_h, placed, padding, self._quality + ) + if result is not None: + result["color"] = color + placed.append(result) + + if len(placed) > len(best_placed): + best_placed = placed + + if len(placed) == len(self._words): + break + + self._layout = best_placed + self._layout_size = (canvas_w, canvas_h) + + def _on_size_allocate(self, widget, allocation): + new_size = (allocation.width, allocation.height) + if new_size != self._layout_size: + if self._resize_timer is not None: + GLib.source_remove(self._resize_timer) + self._resize_timer = GLib.timeout_add(300, self._on_resize_done) + + def _on_resize_done(self): + self._resize_timer = None + self._invalidate() + return False + + def _draw_computing_message(self, cr, w, h): + cr.set_source_rgb(0.97, 0.97, 0.97) + cr.paint() + cr.set_source_rgb(0.5, 0.5, 0.5) + layout = PangoCairo.create_layout(cr) + layout.set_font_description(_make_font_desc(18, style=Pango.Style.ITALIC)) + layout.set_text("Drawing…", -1) + tw, th = layout.get_pixel_size() + cr.move_to(w / 2 - tw / 2, h / 2 - th / 2) + PangoCairo.show_layout(cr, layout) + + def _do_compute_layout(self, w, h): + self._compute_id = None + self._compute_layout(w, h) + self._computing = False + self.queue_draw() + return False + + def _on_draw(self, widget, cr): + alloc = widget.get_allocation() + w, h = alloc.width, alloc.height + + if self._resize_timer is not None: + cr.set_source_rgb(0.97, 0.97, 0.97) + cr.paint() + return + + if self._computing: + self._draw_computing_message(cr, w, h) + if self._compute_id is None: + self._compute_id = GLib.idle_add(self._do_compute_layout, w, h) + return + + if self._layout_size != (w, h): + self._compute_layout(w, h) + + cr.set_source_rgb(0.97, 0.97, 0.97) + cr.paint() + + for pw in self._layout: + hovered = pw["word"] == self._hovered + cr.save() + cr.set_source_rgb(*(self._color_hover if hovered else pw["color"])) + + layout = PangoCairo.create_layout(cr) + layout.set_font_description(_make_font_desc(pw["font_size"])) + layout.set_text(pw["word"], -1) + + p = pw["padding"] + if pw["vertical"]: + cr.translate(pw["ax"] + p + pw["th"], pw["ay"] + p) + cr.rotate(math.pi / 2) + cr.move_to(0, 0) + else: + cr.move_to(pw["ax"] + p, pw["ay"] + p) + + PangoCairo.show_layout(cr, layout) + cr.restore() + + def _on_click_event(self, widget, event): + if self._on_click is None: + return + x, y = event.x, event.y + for pw in reversed(self._layout): + if ( + pw["ax"] <= x <= pw["ax"] + pw["aw"] + and pw["ay"] <= y <= pw["ay"] + pw["ah"] + ): + self._on_click(pw["word"]) + return + + def _on_motion(self, widget, event): + x, y = event.x, event.y + hit = None + for pw in reversed(self._layout): + if ( + pw["ax"] <= x <= pw["ax"] + pw["aw"] + and pw["ay"] <= y <= pw["ay"] + pw["ah"] + ): + hit = pw["word"] + break + if hit != self._hovered: + self._hovered = hit + self.queue_draw() From 0daff2065a1ca94558792731f955339e265ac83d Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Wed, 29 Jul 2026 09:59:29 -0700 Subject: [PATCH 067/156] Merge Add WordClouds gramplet#993 Also created template.pot and pushed POT files to Weblate as part of this merge. --- WordClouds/WordClouds.gpr.py | 6 +- WordClouds/po/template.pot | 84 +++++++++++++++ po/addons.pot | 194 ++++++++++++++++++++++++++++------- 3 files changed, 242 insertions(+), 42 deletions(-) create mode 100644 WordClouds/po/template.pot diff --git a/WordClouds/WordClouds.gpr.py b/WordClouds/WordClouds.gpr.py index ebe25bf93..909ffdd7f 100644 --- a/WordClouds/WordClouds.gpr.py +++ b/WordClouds/WordClouds.gpr.py @@ -24,7 +24,7 @@ name=_("Given Name Word Cloud"), description=_("Gramplet showing all given names as a word cloud"), status=STABLE, - version="1.0.0", + version = '1.0.1', fname="givennamewordcloudgramplet.py", height=300, expand=True, @@ -40,7 +40,7 @@ name=_("Surname Word Cloud"), description=_("Gramplet showing all surnames as a word cloud"), status=STABLE, - version="1.0.0", + version = '1.0.1', fname="surnamewordcloudgramplet.py", height=300, expand=True, @@ -56,7 +56,7 @@ name=_("Place Word Cloud"), description=_("Gramplet showing all places as a word cloud"), status=STABLE, - version="1.0.0", + version = '1.0.1', fname="placewordcloudgramplet.py", height=300, expand=True, diff --git a/WordClouds/po/template.pot b/WordClouds/po/template.pot new file mode 100644 index 000000000..1ee8c7992 --- /dev/null +++ b/WordClouds/po/template.pot @@ -0,0 +1,84 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-07-29 09:56-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" diff --git a/po/addons.pot b/po/addons.pot index 1c7e452d7..7fdf571b1 100644 --- a/po/addons.pot +++ b/po/addons.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,6 +18,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -707,23 +734,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1194,6 +1204,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2346,12 +2360,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5097,6 +5105,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15574,10 +15600,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18621,6 +18643,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19002,6 +19028,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24173,25 +24244,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 From 83d36bb9a3d358ba6aec954533f26a7bc2c6bcc9 Mon Sep 17 00:00:00 2001 From: David Straub Date: Mon, 27 Jul 2026 16:12:24 +0200 Subject: [PATCH 068/156] Refactor & test Gramps Web Sync Addon --- GrampsWebSync/adapters.py | 156 ++++ GrampsWebSync/grampswebsync.py | 934 +++++++++-------------- GrampsWebSync/po/template.pot | 244 +++--- GrampsWebSync/session.py | 625 +++++++++++++++ GrampsWebSync/tests/__init__.py | 51 ++ GrampsWebSync/tests/fakes.py | 359 +++++++++ GrampsWebSync/tests/scenario.py | 401 ++++++++++ GrampsWebSync/tests/test_adapters.py | 94 +++ GrampsWebSync/tests/test_errors.py | 263 +++++++ GrampsWebSync/tests/test_sync_flow.py | 320 ++++++++ GrampsWebSync/tests/test_transitions.py | 129 ++++ GrampsWebSync/tests/test_view_mapping.py | 72 ++ GrampsWebSync/webapihandler.py | 2 +- 13 files changed, 2928 insertions(+), 722 deletions(-) create mode 100644 GrampsWebSync/adapters.py create mode 100644 GrampsWebSync/session.py create mode 100644 GrampsWebSync/tests/__init__.py create mode 100644 GrampsWebSync/tests/fakes.py create mode 100644 GrampsWebSync/tests/scenario.py create mode 100644 GrampsWebSync/tests/test_adapters.py create mode 100644 GrampsWebSync/tests/test_errors.py create mode 100644 GrampsWebSync/tests/test_sync_flow.py create mode 100644 GrampsWebSync/tests/test_transitions.py create mode 100644 GrampsWebSync/tests/test_view_mapping.py diff --git a/GrampsWebSync/adapters.py b/GrampsWebSync/adapters.py new file mode 100644 index 000000000..d941ac571 --- /dev/null +++ b/GrampsWebSync/adapters.py @@ -0,0 +1,156 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2021-2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Production implementations of the :mod:`session` ports.""" + +from __future__ import annotations + +import logging +import os +import time +from collections.abc import Callable +from typing import Any + +from gi.repository import GLib +from gramps.gen.config import config as configman +from gramps.gen.utils.file import media_path_full + +LOG = logging.getLogger("grampswebsync") + + +def get_password(service: str, username: str) -> str | None: + """Return the stored password for ``username``, if a keyring is available. + + :param service: Keyring service name; the server URL is used. + :param username: The account whose password is wanted. + :returns: The password, or ``None`` if unavailable. + """ + LOG.debug("Retrieving password for user %s", username) + try: + import keyring + except ImportError: + LOG.warning("Keyring is not installed, cannot retrieve password.") + return None + return keyring.get_password(service, username) + + +def set_password(service: str, username: str, password: str) -> None: + """Store ``password`` in the keyring, if one is available.""" + try: + import keyring + except ImportError: + return + LOG.debug("Storing password for user %s", username) + keyring.set_password(service, username, password) + + +class ConfigCredentialStore: + """Credentials in the Gramps config file, password in the system keyring.""" + + def __init__(self) -> None: + self.config = configman.register_manager("webapisync") + self.config.register("credentials.url", "") + self.config.register("credentials.username", "") + self.config.register("credentials.timestamp", 0) + self.config.load() + + def get_url(self) -> str: + return self.config.get("credentials.url") + + def get_username(self) -> str: + return self.config.get("credentials.username") + + def get_password(self) -> str | None: + url = self.get_url() + username = self.get_username() + if not url or not username: + return None + return get_password(url, username) + + def get_timestamp(self) -> float: + return self.config.get("credentials.timestamp") + + def set_timestamp(self, timestamp: float) -> None: + LOG.debug("Recording last successful sync at %s", timestamp) + self.config.set("credentials.timestamp", timestamp) + self.config.save() + + def save_credentials(self, url: str, username: str, password: str) -> None: + """Persist the credentials, resetting the sync time if the URL changed.""" + if url != self.get_url(): + self.config.set("credentials.timestamp", 0) + self.config.set("credentials.url", url) + self.config.set("credentials.username", username) + set_password(url, username, password) + self.config.save() + + +class GrampsMediaStore: + """Resolves media paths against the open Gramps database's media path. + + :param db: The local database whose media base path applies. + """ + + def __init__(self, db) -> None: + self.db = db + + def full_path(self, media: Any) -> str: + """Return the absolute path of ``media``'s file.""" + return media_path_full(self.db, media.get_path()) + + def exists(self, media: Any) -> bool: + """Whether ``media``'s file is present on disk.""" + return os.path.exists(self.full_path(media)) + + +class GLibTaskRunner: + """Defers a task to the GTK main loop. + + The task must not run on a worker thread: it drives Gramps progress + through the GUI :class:`gramps.gui.user.User`, which touches widgets, and + GTK is not thread-safe -- doing so segfaults inside ``diff_dbs``. + :func:`GLib.idle_add` keeps the work on the main loop while still letting + the caller return so the assistant can paint the progress page first. + """ + + def run( + self, + func: Callable[[], Any], + on_success: Callable[[Any], None], + on_error: Callable[[BaseException], None], + ) -> None: + """Schedule ``func`` on the main loop and dispatch the outcome there.""" + + def once() -> bool: + try: + result = func() + except BaseException as exc: # noqa: BLE001 -- reported, not swallowed + on_error(exc) + else: + on_success(result) + return False # run once + + GLib.idle_add(once) + + +class SystemClock: + """The wall clock.""" + + def now(self) -> float: + """Return the current POSIX timestamp.""" + return time.time() diff --git a/GrampsWebSync/grampswebsync.py b/GrampsWebSync/grampswebsync.py index 2bc090295..918d586cd 100644 --- a/GrampsWebSync/grampswebsync.py +++ b/GrampsWebSync/grampswebsync.py @@ -1,6 +1,6 @@ # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2021-2024 David Straub +# Copyright (C) 2021-2026 David Straub # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,19 +17,27 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -"""Gramps addon to synchronize with a Gramps Web server.""" +"""Gramps addon to synchronize with a Gramps Web server. + +Provides :class:`GrampsWebSyncTool`, a :class:`Gtk.Assistant` presenting a +:class:`session.SyncSession`. :data:`PAGE_FOR_STATE` maps each +:class:`session.State` to an assistant page and :func:`error_message` +localizes a :class:`session.ErrorKind`. + +The synchronization itself lives in :mod:`session`. +""" from __future__ import annotations import logging -import os -import threading -from collections.abc import Callable -from datetime import datetime -from typing import Any -from urllib.error import HTTPError, URLError from urllib.parse import urlparse +from adapters import ( + ConfigCredentialStore, + GLibTaskRunner, + GrampsMediaStore, + SystemClock, +) from const import ( C_ADD_LOC, C_ADD_REM, @@ -44,24 +52,23 @@ MODE_RESET_TO_REMOTE, Actions, ) -from diffhandler import ( - WebApiSyncDiffHandler, - changes_to_actions, - has_local_actions, - has_remote_actions, -) -from gi.repository import GLib, Gtk -from gramps.gen.config import config as configman +from diffhandler import changes_to_actions, has_local_actions, has_remote_actions +from gi.repository import Gtk from gramps.gen.const import GRAMPS_LOCALE as glocale -from gramps.gen.db import DbTxn -from gramps.gen.db.utils import import_as_dict -from gramps.gen.errors import HandleError from gramps.gen.lib import Tag -from gramps.gen.utils.file import media_path_full from gramps.gui.dialog import QuestionDialog2 from gramps.gui.managedwindow import ManagedWindow from gramps.gui.plug.tool import BatchTool, ToolOptions -from webapihandler import WebApiHandler, transaction_to_json +from session import ( + STATUS_COMPARING, + STATUS_FETCHING, + STATUS_LOCAL_APPLIED, + ErrorKind, + State, + SyncSession, + next_state, +) +from webapihandler import WebApiHandler assert glocale is not None # for type checker try: @@ -75,65 +82,115 @@ LOG = logging.getLogger("grampswebsync") -def get_password(service: str, username: str) -> str | None: - """If keyring is installed, return the user's password or None.""" - LOG.debug("Retrieving password for user %s", username) - try: - import keyring - except ImportError: - LOG.warning("Keyring is not installed, cannot retrieve password.") - return None - return keyring.get_password(service, username) - - -def set_password(service: str, username: str, password: str) -> None: - """If keyring is installed, store the user's password.""" - try: - import keyring - except ImportError: - return None - LOG.debug("Storing password for user %s", username) - keyring.set_password(service, username, password) +#: Assistant page indices, in the order the pages are appended. +PAGE_INTRO = 0 +PAGE_LOGIN = 1 +PAGE_COMPARING = 2 +PAGE_REVIEW_CHANGES = 3 +PAGE_APPLYING = 4 +PAGE_REVIEW_FILES = 5 +PAGE_TRANSFERRING = 6 +PAGE_CONCLUSION = 7 + +#: The one place that knows how flow states correspond to assistant pages. +#: Both terminal states share the conclusion page, which renders either the +#: summary or the error. +PAGE_FOR_STATE: dict[State, int] = { + State.INTRO: PAGE_INTRO, + State.LOGIN: PAGE_LOGIN, + State.COMPARING: PAGE_COMPARING, + State.REVIEW_CHANGES: PAGE_REVIEW_CHANGES, + State.APPLYING: PAGE_APPLYING, + State.REVIEW_FILES: PAGE_REVIEW_FILES, + State.TRANSFERRING: PAGE_TRANSFERRING, + State.DONE: PAGE_CONCLUSION, + State.FAILED: PAGE_CONCLUSION, +} + + +def error_message(kind: ErrorKind, detail: str = "") -> str: + """Return the localized message for an error kind. + + Translation lives here rather than in :mod:`session` so the flow logic can + be asserted on stable enum values instead of translated prose. + + :param kind: The classification recorded by the session. + :param detail: Optional extra context, e.g. an HTTP status. + :returns: A message suitable for display. + """ + messages = { + ErrorKind.AUTH_FAILED: _( + "Authentication failed. Please check your username and password." + ), + ErrorKind.FORBIDDEN: _( + "Access forbidden. Please check username and password." + ), + ErrorKind.NOT_FOUND: _("GrampsWeb service not found. Please check the URL."), + ErrorKind.RATE_LIMITED: _( + "Too many requests, please try again in a few seconds." + ), + ErrorKind.TREE_DISABLED: _("GrampsWeb tree is disabled."), + ErrorKind.CONNECTION_FAILED: _( + "Connection failed. Please check the URL and your internet connection." + ), + ErrorKind.INVALID_RESPONSE: _( + "Invalid server response. Please check the URL." + ), + ErrorKind.INSUFFICIENT_PERMISSIONS: _( + "Your user does not have sufficient server permissions to use sync." + ), + ErrorKind.XML_IMPORT_FAILED: _("Failed importing downloaded XML file."), + ErrorKind.CONFLICT: _( + "Unable to synchronize changes to server: objects have been modified." + ), + ErrorKind.APPLY_FAILED: _("Unexpected error while applying changes."), + } + if kind is ErrorKind.SERVER_ERROR: + return _("Server error %s. Please check your connection.") % detail + if kind is ErrorKind.UNEXPECTED: + return _("Unexpected error: %s") % detail + return messages.get(kind, _("Unexpected error: %s") % detail) class GrampsWebSyncTool(BatchTool, ManagedWindow): - """Main class for the Gramps Web Sync tool.""" + """Assistant presenting a :class:`session.SyncSession` to the user.""" def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: - """Initialize GUI.""" + """Build the assistant and the session behind it.""" LOG.debug("Initializing Gramps Web Sync addon.") BatchTool.__init__(self, dbstate, user, options_class, name) ManagedWindow.__init__(self, user.uistate, [], self.__class__) self.dbstate = dbstate - self.callback = self.uistate.pulse_progressbar - self.config = configman.register_manager("webapisync") - self.config.register("credentials.url", "") - self.config.register("credentials.username", "") - self.config.register("credentials.timestamp", 0) - self.config.load() + self.credentials = ConfigCredentialStore() + self.session = SyncSession( + db=dbstate.db, + user=self._user, + backend_factory=self._make_backend, + credentials=self.credentials, + media=GrampsMediaStore(dbstate.db), + runner=GLibTaskRunner(), + clock=SystemClock(), + listener=self, + ) self.assistant = Gtk.Assistant() self.set_window(self.assistant, None, _("Gramps Web Sync")) self.setup_configs("interface.webapisync", 780, 600) - self.assistant.connect("close", self.do_close) - self.assistant.connect("cancel", self.do_close) - self.assistant.connect("apply", self.apply) + self.assistant.connect("close", self.do_close, "close") + self.assistant.connect("cancel", self.do_close, "cancel") self.assistant.connect("prepare", self.prepare) self.intro = IntroductionPage(self.assistant) self.add_page(self.intro, Gtk.AssistantPageType.INTRO, _("Introduction")) - self.url = self.config.get("credentials.url") - self.username = self.config.get("credentials.username") - self.password = self.get_password() self.loginpage = LoginPage( self.assistant, - url=self.url, - username=self.username, - password=self.password, + url=self.credentials.get_url(), + username=self.credentials.get_username(), + password=self.credentials.get_password(), ) self.add_page(self.loginpage, Gtk.AssistantPageType.CONTENT, _("Login")) @@ -151,16 +208,12 @@ def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: self.sync_progress_page = SyncProgressPage(self.assistant) self.add_page( - self.sync_progress_page, - Gtk.AssistantPageType.PROGRESS, - _("Summary"), + self.sync_progress_page, Gtk.AssistantPageType.PROGRESS, _("Summary") ) self.file_confirmation = FileConfirmationPage(self.assistant) self.add_page( - self.file_confirmation, - Gtk.AssistantPageType.CONFIRM, - _("Media Files"), + self.file_confirmation, Gtk.AssistantPageType.CONFIRM, _("Media Files") ) self.file_progress_page = FileProgressPage(self.assistant) @@ -176,447 +229,154 @@ def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: self.show() self.assistant.set_forward_page_func(self.forward_page, None) - self._api: WebApiHandler | None = None - - self.db1 = dbstate.db - self.db2 = None - self._closing = False - self._download_timestamp = 0 - self._changes: Actions | None = None - self._sync: WebApiSyncDiffHandler | None = None - self.files_missing_local: list[tuple[str, str]] = [] - self.files_missing_remote: list[tuple[str, str]] = [] - self.uploaded: dict[str, bool] = {} - self.downloaded: dict[str, bool] = {} - - @property - def api(self) -> WebApiHandler: - if self._api is None: - raise ValueError("No WebApiHandler found") # shouldn't happen! - return self._api - - @property - def sync(self) -> WebApiSyncDiffHandler: - if self._sync is None: - raise ValueError("No WebApiSyncDiffHandler found") # shouldn't happen! - return self._sync - - @property - def changes(self) -> Actions: - if self._changes is None: - raise ValueError("No change actions found") # shouldn't happen! - return self._changes - + # -------------------------------------------------------- + # Window management + # -------------------------------------------------------- def build_menu_names(self, obj): # type: ignore """Override :class:`.ManagedWindow` method.""" return (_("Gramps Web Sync"), None) - def do_close(self, assistant): - """Close the assistant.""" - LOG.debug("Closing Gramps Web Sync addon.") - self._closing = True - if self.db2 is not None: - LOG.debug("Closing in-memory remote database.") - self.db2.close() - self.db2 = None - # Clear the diff handler which holds references to both db1 and db2 - self._sync = None - self._changes = None + def add_page(self, page, page_type, title=""): + """Append a page to the assistant.""" + page.show_all() + self.assistant.append_page(page) + self.assistant.set_page_title(page, title) + self.assistant.set_page_type(page, page_type) + + def do_close(self, assistant, signal_name="?"): + """Close the assistant and release the session's resources. + + :param assistant: The assistant emitting the signal. + :param signal_name: Which signal fired, ``close`` or ``cancel``. + """ + LOG.debug( + "Closing Gramps Web Sync addon (signal=%s, page=%s, state=%s).", + signal_name, + assistant.get_current_page(), + self.session.state.name, + ) + self.session.cancel() position = self.window.get_position() # crock self.assistant.hide() self.window.move(position[0], position[1]) self.close() - def forward_page(self, page, data): - """Specify the next page to be displayed.""" - LOG.debug(f"Moving to next page from page {page}.") - if self.conclusion.error: - LOG.debug("Skipping to last page due to error.") - return 7 - if page == 2 and self._changes is not None and len(self.changes) == 0: - LOG.debug("Skipping to media sync as databases are in sync.") - return 4 - if page == 5 and self.conclusion.unchanged: - LOG.debug("Skipping to last page as media files are in sync.") - return 7 - return page + 1 + def _make_backend(self, url: str, username: str, password: str) -> WebApiHandler: + """Build the real Web API handler. Injected into the session.""" + return WebApiHandler(url, username, password, None) + + # -------------------------------------------------------- + # SessionListener + # -------------------------------------------------------- + def on_state_changed(self, state: State) -> None: + """Follow the session to the page representing ``state``.""" + target = PAGE_FOR_STATE[state] + if self.assistant.get_current_page() != target: + self.assistant.set_current_page(target) + + def on_progress(self, kind: str, fraction: float) -> None: + """Render a progress update from the session.""" + if kind == "api": + self.sync_progress_page.update_api_progress(fraction) + else: + self.file_progress_page.update_progress(kind, fraction) + self._pump() - def add_page(self, page, page_type, title=""): - """Add a page to the assistant.""" - page.show_all() - self.assistant.append_page(page) - self.assistant.set_page_title(page, title) - self.assistant.set_page_type(page, page_type) + def on_status(self, stage: str) -> None: + """Render a status update from the session.""" + self._render_status(stage) + self._pump() - def handle_done_syncing_dbs(self): - """Handle the completion of syncing the databases.""" - self.save_timestamp() - self.sync_progress_page.handle_done_syncing_dbs() - self.files_missing_local = self.get_missing_files_local() - self.assistant.next_page() + @staticmethod + def _pump() -> None: + """Redraw now. - def prepare(self, assistant, page): - """Run page preparation code.""" - page.update_complete() - if page == self.diff_progress_page: - # Clear any previous login error when starting fresh - self.loginpage.clear_error() - - # Try to connect and authenticate - self.save_credentials() - url, username, password = self.get_credentials() - if not self.test_connection(url, username, password): - # Connection failed, go back to login page - self.assistant.set_current_page(1) # Login page index - return None - - if "ViewPrivate" not in self.api.get_permissions(): - self.loginpage.show_error( - _( - "Your user does not have sufficient server permissions to use sync." - ) - ) - self.assistant.set_current_page(1) # Go back to login page - return None + The session's steps run on the main loop, so without this the label + and bar would not repaint until the whole step finished. + """ + while Gtk.events_pending(): + Gtk.main_iteration() + def _render_status(self, stage: str) -> None: + """Show the message for ``stage``.""" + if stage == STATUS_FETCHING: self.diff_progress_page.label.set_text(_("Fetching remote data...")) - t = threading.Thread(target=self.async_compare_dbs) - t.start() - elif page == self.confirmation: - self.confirmation.prepare(self.changes) - elif page == self.sync_progress_page: - self.assistant.commit() # just erases the visited page history - actions = changes_to_actions(self.changes, self.confirmation.sync_mode) - self.sync_progress_page.prepare(actions) - if len(actions) == 0: - self.handle_done_syncing_dbs() - else: - try: - self.commit_all_actions(actions) - except Exception as e: - self.handle_error( - _("Unexpected error while applying changes.") + f" {e}" - ) + elif stage == STATUS_COMPARING: + self.diff_progress_page.label.set_text( + _("Comparing local and remote data...") + ) + elif stage == STATUS_LOCAL_APPLIED: + self.sync_progress_page.label.set_text( + _("Successfully applied changes to local database.") + ) - # now, get missing media files - elif page == self.file_confirmation: - if self.files_missing_local: - LOG.debug( - "The following media files are missing on the local side: %s", - ", ".join([gramps_id for gramps_id, _ in self.files_missing_local]), - ) - else: - LOG.debug("No files missing locally.") - self.files_missing_remote = self.get_missing_files_remote() - if self.files_missing_remote: - LOG.debug( - "The following media files are missing on the remote side: %s", - ", ".join( - [gramps_id for gramps_id, _ in self.files_missing_remote] - ), - ) - else: - LOG.debug("No files missing remotely.") - if not self.files_missing_local and not self.files_missing_remote: - self.handle_files_unchanged() + # -------------------------------------------------------- + # Navigation + # -------------------------------------------------------- + def forward_page(self, page, data): + """Return the page index the session's flow says comes next.""" + return PAGE_FOR_STATE[next_state(self.session.state, self.session)] + + def prepare(self, assistant, page): + """Render a page as it is shown, and fire any intent it triggers. + + Every intent is guarded on the session's current state, so a page + being prepared more than once -- which happens whenever the session + navigates to the page it is already on -- cannot start the same work + twice. + """ + page.update_complete() + + if page is self.loginpage: + if self.session.state is State.INTRO: + self.session.begin() + if self.session.login_error is not None: + error = self.session.login_error + self.loginpage.show_error(error_message(error.kind, error.detail)) else: - self.file_confirmation.prepare( - self.files_missing_local, self.files_missing_remote + self.loginpage.clear_error() + + elif page is self.diff_progress_page: + if self.session.state is State.LOGIN: + self.loginpage.clear_error() + url = self.sanitize_url(self.loginpage.url.get_text()) + self.session.submit_credentials( + url, + self.loginpage.username.get_text(), + self.loginpage.password.get_text(), ) - elif page == self.file_progress_page: - self.file_progress_page.prepare( - self.files_missing_local, self.files_missing_remote + + elif page is self.confirmation: + self.confirmation.prepare(self.session.changes) + + elif page is self.sync_progress_page: + if self.session.state is State.REVIEW_CHANGES: + self.assistant.commit() # erases the visited page history + mode = self.confirmation.sync_mode + self.sync_progress_page.prepare(self.session, mode) + self.session.confirm_changes(mode) + + elif page is self.file_confirmation: + self.file_confirmation.prepare( + self.session.missing_local, self.session.missing_remote ) - t = threading.Thread(target=self.async_transfer_media) - t.start() - elif page == self.conclusion: - if self.conclusion.error: - pass - elif self.conclusion.unchanged: - text = _("Media files are in sync.") - self.conclusion.label.set_text(text) - LOG.info("Media files are in sync.") - else: - text = "" - if self.downloaded: - ok = sum([b for gid, b in self.downloaded.items()]) - nok = sum([not b for gid, b in self.downloaded.items()]) - if ok: - text += _("Successfully downloaded %s media files.") % ok - text += " " - if nok: - text += _("Encountered %s errors during download.") % nok - text += " " - if self.uploaded: - ok = sum([b for gid, b in self.uploaded.items()]) - nok = sum([not b for gid, b in self.uploaded.items()]) - if ok: - text += _("Successfully uploaded %s media files.") % ok - text += " " - if nok: - text += _("Encountered %s errors during upload.") % nok - self.conclusion.label.set_text(text) - - self.conclusion.set_complete() - - def test_connection(self, url: str, username: str, password: str) -> bool: - """Test the connection and authentication. Return True if successful.""" - try: - # Try to create API handler - self._api = WebApiHandler(url, username, password, None) - - # Test the connection by making a simple API call - self.api.get_permissions() - return True - except HTTPError as exc: - if exc.code == 401: - self.loginpage.show_error( - _("Authentication failed. Please check your username and password.") - ) - elif exc.code == 403: - self.loginpage.show_error( - _("Access forbidden. Please check username and password.") - ) - elif exc.code == 404: - self.loginpage.show_error( - _("GrampsWeb service not found. Please check the URL.") - ) - elif exc.code == 429: - self.loginpage.show_error( - _("Too many requests, please try again in a few seconds.") - ) - elif exc.code == 503: - self.loginpage.show_error(_("GrampsWeb tree is disabled.")) - else: - self.loginpage.show_error( - _("Server error %s. Please check your connection.") % exc.code + elif page is self.file_progress_page: + if self.session.state is State.REVIEW_FILES: + self.file_progress_page.prepare( + self.session.missing_local, self.session.missing_remote ) - return False - except URLError: - self.loginpage.show_error( - _( - "Connection failed. Please check the URL and your internet connection." - ) - ) - return False - except ValueError: - self.loginpage.show_error( - _("Invalid server response. Please check the URL.") - ) - return False - except Exception as e: - self.loginpage.show_error(_("Unexpected error: %s") % str(e)) - return False - - def handle_files_unchanged(self): - self.conclusion.unchanged = True - self.assistant.next_page() - - def apply(self, assistant): - """Apply the changes.""" - page_number = assistant.get_current_page() - page = assistant.get_nth_page(page_number) - if page == self.confirmation: - pass - elif page == self.file_confirmation: - pass - - def download_files(self): - """Download media files missing locally.""" - if not self.files_missing_local: - return - res = {} - for gramps_id, handle in self.files_missing_local: - LOG.debug("Downloading file %s", gramps_id) - self.downloaded[gramps_id] = self._download_file(handle) - self._update_file_progress() - return res - - def _update_file_progress(self): - """Update the file progress bars.""" - self.file_progress_page.update_progress( - self.files_missing_local, - self.files_missing_remote, - self.downloaded, - self.uploaded, - ) - # force updating progress bar - while Gtk.events_pending(): - Gtk.main_iteration() + self.session.confirm_files() - def _download_file(self, handle): - """Download a single media file.""" - try: - obj = self.db1.get_media_from_handle(handle) - except HandleError: - self.handle_error(_("Error accessing media object.")) - return False - path = media_path_full(self.db1, obj.get_path()) - try: - return self.api.download_media_file(handle=handle, path=path) - except Exception as e: - LOG.warning(f"Failed to download media file {obj.gramps_id}: {e}") - return False - - def upload_files(self): - """Upload media files missing remotely.""" - if not self.files_missing_remote: - return - res = {} - for gramps_id, handle in self.files_missing_remote: - LOG.debug("Uploading file %s", gramps_id) - self.uploaded[gramps_id] = self._upload_file(handle) - self._update_file_progress() - return res - - def _upload_file(self, handle): - """Upload a single media file.""" - try: - obj = self.db1.get_media_from_handle(handle) - except HandleError: - self.handle_error(_("Error accessing media object.")) - return - path = media_path_full(self.db1, obj.get_path()) - return self.api.upload_media_file(handle=handle, path=path) - - def get_password(self): - """Get a stored password.""" - url = self.config.get("credentials.url") - username = self.config.get("credentials.username") - if not url or not username: - return None - return get_password(url, username) - - def handle_error(self, message): - """Handle an error message during sync.""" - LOG.warning(message) - self.conclusion.error = True - self.assistant.next_page() - self.conclusion.label.set_text(message) - self.conclusion.set_complete() - - def handle_unchanged(self): - """Return a message if nothing has changed.""" - self.save_timestamp() - self.assistant.next_page() - - def async_compare_dbs(self): - """Download the remote data and import it to an in-memory database.""" - # store timestamp just before downloading the XML - self._download_timestamp = datetime.now().timestamp() - GLib.idle_add(self.get_diff_actions) - - def get_diff_actions(self) -> None: - """Download the remote data, import it and compare it to local.""" - if self._closing: - return - LOG.info("Downloading Gramps XML file.") - path = self.handle_server_errors(self.api.download_xml) - if path is None: - return - LOG.debug(f"The file name of the downloaded file is: {path}") - LOG.debug("Importing Gramps XML file.") - db2 = import_as_dict(str(path), self._user) - if db2 is None: - self.handle_error(_("Failed importing downloaded XML file.")) - return - LOG.debug("Successfully imported Gramps XML file.") - path.unlink() # delete temporary file - self.db2 = db2 - self.diff_progress_page.label.set_text(_("Comparing local and remote data...")) - LOG.info("Comparing local and remote data...") - timestamp = self.config.get("credentials.timestamp") or None - from datetime import datetime + elif page is self.conclusion: + self.conclusion.prepare(self.session) - LOG.debug( - "Loading last sync timestamp from config: %s (%s)", - timestamp, - ( - datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S %Z") - if timestamp - else "None" - ), - ) - self._sync = WebApiSyncDiffHandler( - self.db1, self.db2, user=self._user, last_synced=timestamp - ) - self._changes = self.sync.get_changes() - self.diff_progress_page.label.set_text("") - self.diff_progress_page.set_complete() - if len(self.changes) == 0: - LOG.info("Databases are in sync.") - self.handle_unchanged() - else: - self.assistant.next_page() - - def async_transfer_media(self): - """Upload/download media files.""" - GLib.idle_add(self._async_transfer_media) - - def _async_transfer_media(self): - """Upload/download media files.""" - if self._closing: - return - self.handle_server_errors(self.download_files) - if self.conclusion.error: - return - self.handle_server_errors(self.upload_files) - if self.conclusion.error: - return - self.file_progress_page.set_complete() - self.assistant.next_page() - - def handle_server_errors(self, callback: Callable, *args) -> None: - """Handle server errors while executing a function.""" - try: - return callback(*args) - except HTTPError as exc: - if exc.code == 401: - self.handle_error(_("Server authorization error.")) - elif exc.code == 403: - self.handle_error( - _("Server authorization error: insufficient permissions.") - ) - elif exc.code == 404: - self.handle_error(_("Error: URL not found.")) - elif exc.code == 409: - self.handle_error( - _( - "Unable to synchronize changes to server: objects have been modified." - ) - ) - else: - self.handle_error(_("Error %s while connecting to server.") % exc.code) - return None - except URLError: - self.handle_error(_("URL error while connecting to server.")) - return None - except ValueError as exc: - self.handle_error( - f"{_('Unable to synchronize changes to server.')} ({exc})" - ) - return None - - def save_credentials(self) -> None: - """Save the login credentials.""" - url = self.loginpage.url.get_text() - url = self.sanitize_url(url) - if url is None: - self.handle_error("No URL provided") - return - username = self.loginpage.username.get_text() - password = self.loginpage.password.get_text() - if url != self.config.get("credentials.url"): - # if URL changed, clear last sync timestamp - self.config.set("credentials.timestamp", 0) - self.config.set("credentials.url", url) - self.config.set("credentials.username", username) - set_password(url, username, password) - self.config.save() - - def sanitize_url(self, url: str) -> str | None: - """Warn if http and prepend https if missing.""" + def sanitize_url(self, url: str) -> str: + """Prepend https if no scheme is given, and warn about plain http. + + :param url: The URL as typed by the user. + :returns: The URL to actually use. + """ parsed_url = urlparse(url) if parsed_url.scheme == "": # if no httpX given, prepend https! @@ -638,84 +398,6 @@ def sanitize_url(self, url: str) -> str | None: return url.replace("http", "https") return url - def get_credentials(self): - """Get a tuple of URL, username, and password.""" - return ( - self.config.get("credentials.url"), - self.config.get("credentials.username"), - self.loginpage.password.get_text(), - ) - - def commit_all_actions(self, actions: Actions) -> None: - """Commit all changes to the databases.""" - LOG.info("Committing all changes to the databases.") - msg = "Apply Gramps Web Sync changes" - with DbTxn(msg, self.sync.db1) as trans1: - with DbTxn(msg, self.sync.db2) as trans2: - if has_local_actions(actions): - LOG.debug("Committing changes to local database.") - else: - LOG.debug("No changes to apply to local database.") - self.sync.commit_actions(actions, trans1, trans2) - self.sync_progress_page.handle_local_sync_complete(actions) - # force the sync for all modes: the server-side "object has changed" - # check compares against the XML-round-tripped object, which often - # differs from the live server object due to serialization artifacts, - # causing false-positive 409 conflicts even when no real concurrent - # edit has occurred. - force = True - lang = self.api.get_lang() - payload = transaction_to_json(trans2, lang) - GLib.idle_add(self.async_commit_actions_to_remote, payload, force) - - def async_commit_actions_to_remote( - self, payload: dict[str, "Any"], force: bool - ) -> None: - """Commit all changes to the remote database.""" - GLib.idle_add(self._async_commit_actions_to_remote, payload, force) - - def _async_commit_actions_to_remote( - self, payload: dict[str, "Any"], force: bool - ) -> None: - """Upload/download media files.""" - if self._closing: - return - LOG.debug("Committing changes to remote database.") - self.handle_server_errors( - self.api.commit, - payload, - force, - self.sync_progress_page.update_api_progress, - ) - if self.conclusion.error: - return - self.handle_done_syncing_dbs() - - def save_timestamp(self): - """Save last sync timestamp.""" - # self.config.set("credentials.timestamp", self._download_timestamp) - timestamp = datetime.now().timestamp() - LOG.debug( - "Saving current time stamp (%s) as last successful sync time (%s).", - timestamp, - datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S %Z"), - ) - self.config.set("credentials.timestamp", timestamp) - self.config.save() - - def get_missing_files_local(self) -> list[tuple[str, str]]: - """Get a list of media files missing locally.""" - return [ - (media.gramps_id, media.handle) - for media in self.db1.iter_media() - if not os.path.exists(media_path_full(self.db1, media.get_path())) - ] - - def get_missing_files_remote(self): - """Get a list of media files missing remotely.""" - missing_files = self.handle_server_errors(self.api.get_missing_files) or [] - return [(media["gramps_id"], media["handle"]) for media in missing_files] - class Page(Gtk.Box): """Page base class.""" @@ -944,6 +626,7 @@ def on_radio_button_toggled(self, button, name): def prepare(self, changes: Actions): """Convert the changes list to a tree store.""" + self.store.clear() # this page may be prepared more than once change_labels = { _("Local changes"): { _("Added"): C_ADD_LOC, @@ -1026,11 +709,17 @@ def update_api_progress(self, progress: float) -> None: self.progressbar_api.set_fraction(progress) else: self.progressbar_api.pulse() - # force updating progress bar - while Gtk.events_pending(): - Gtk.main_iteration() - def prepare(self, actions: Actions): + def prepare(self, session: SyncSession, sync_mode: int): + """Describe the work about to be done. + + Called before the session applies anything, so the actions are derived + here from the mode the user just chose. + + :param session: The session holding the pending changes. + :param sync_mode: The mode selected on the confirmation page. + """ + actions = changes_to_actions(session.changes, sync_mode) if len(actions) == 0: self.label.set_text(_("Both trees are the same.")) self.label_progressbar_api.hide() @@ -1053,16 +742,6 @@ def prepare(self, actions: Actions): ) self.progressbar_api.hide() - def handle_local_sync_complete(self, actions: Actions) -> None: - """Handle completion of local sync.""" - if not has_local_actions(actions): - return - self.label.set_text(_("Successfully applied changes to local database.")) - - def handle_done_syncing_dbs(self) -> None: - """Handle completion of syncing the databases.""" - self.media_label.show() - class FileConfirmationPage(Page): """File sync confirmation page.""" @@ -1086,6 +765,8 @@ def __init__(self, assistant): self.pack_start(scrolled_window, True, True, 0) def prepare(self, missing_local, missing_remote): + """List the files that would be transferred.""" + self.store.clear() # this page may be prepared more than once iter_local = self.store.append(None, [_("Missing locally")]) for gramps_id, handle in missing_local: self.store.append(iter_local, [gramps_id]) @@ -1127,7 +808,14 @@ def prepare(self, files_missing_local, files_missing_remote): else: self.label1.show() self.progressbar1.show() - self.label1.set_text(_("Downloading %s media file(s)") % n_down) + self.label1.set_text( + ngettext( + "Downloading %s media file", + "Downloading %s media files", + n_down, + ) + % n_down + ) n_up = len(files_missing_remote) if not n_up: self.label2.hide() @@ -1135,29 +823,32 @@ def prepare(self, files_missing_local, files_missing_remote): else: self.label2.show() self.progressbar2.show() - self.label2.set_text(_("Uploading %s media file(s)") % n_up) + self.label2.set_text( + ngettext( + "Uploading %s media file", + "Uploading %s media files", + n_up, + ) + % n_up + ) - def update_progress( - self, files_missing_local, files_missing_remote, downloaded, uploaded - ): - """Update the progress bar.""" - n_down = len(files_missing_local) - n_up = len(files_missing_remote) - i_down = len(downloaded) - i_up = len(uploaded) - if n_down: - self.progressbar1.set_fraction(i_down / n_down) - if n_up: - self.progressbar2.set_fraction(i_up / n_up) + def update_progress(self, kind: str, fraction: float): + """Update the download or upload progress bar. + + :param kind: Either ``"download"`` or ``"upload"``. + :param fraction: Completed share of that transfer, in ``[0, 1]``. + """ + if kind == "download": + self.progressbar1.set_fraction(fraction) + elif kind == "upload": + self.progressbar2.set_fraction(fraction) class ConclusionPage(Page): - """The conclusion page.""" + """The conclusion page, reporting either the outcome or the error.""" def __init__(self, assistant): super().__init__(assistant) - self.error = False - self.unchanged = False label = Gtk.Label(label="") label.set_line_wrap(True) label.set_use_markup(True) @@ -1165,6 +856,67 @@ def __init__(self, assistant): self.label = label self.pack_start(self.label, False, False, 0) + def prepare(self, session: SyncSession) -> None: + """Render the final message for ``session``.""" + if session.error is not None: + self.label.set_text( + error_message(session.error.kind, session.error.detail) + ) + elif not session.downloaded and not session.uploaded: + self.label.set_text(_("Media files are in sync.")) + LOG.info("Media files are in sync.") + else: + self.label.set_text(self._transfer_summary(session)) + self.set_complete() + + @staticmethod + def _transfer_summary(session: SyncSession) -> str: + """Summarize how many media files moved, and how many failed.""" + text = "" + if session.downloaded: + ok = sum(session.downloaded.values()) + nok = sum(not v for v in session.downloaded.values()) + if ok: + text += ( + ngettext( + "Successfully downloaded %s media file.", + "Successfully downloaded %s media files.", + ok, + ) + % ok + + " " + ) + if nok: + text += ( + ngettext( + "Encountered %s error during download.", + "Encountered %s errors during download.", + nok, + ) + % nok + + " " + ) + if session.uploaded: + ok = sum(session.uploaded.values()) + nok = sum(not v for v in session.uploaded.values()) + if ok: + text += ( + ngettext( + "Successfully uploaded %s media file.", + "Successfully uploaded %s media files.", + ok, + ) + % ok + + " " + ) + if nok: + text += ngettext( + "Encountered %s error during upload.", + "Encountered %s errors during upload.", + nok, + ) % nok + return text + class GrampsWebSyncOptions(ToolOptions): """Options for Gramps Web Sync.""" diff --git a/GrampsWebSync/po/template.pot b/GrampsWebSync/po/template.pot index e48ad7e0b..61da0f9b4 100644 --- a/GrampsWebSync/po/template.pot +++ b/GrampsWebSync/po/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"POT-Creation-Date: 2026-07-27 09:45+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,9 +16,10 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:179 +#: GrampsWebSync/grampswebsync.py:237 msgid "Gramps Web Sync" msgstr "" @@ -26,164 +27,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:127 -msgid "Introduction" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:138 -msgid "Login" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:144 GrampsWebSync/grampswebsync.py:170 -msgid "Progress Information" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:149 -msgid "Final confirmation" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:156 GrampsWebSync/grampswebsync.py:174 -msgid "Summary" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:163 -msgid "Media Files" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." +#: GrampsWebSync/grampswebsync.py:123 +msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." +#: GrampsWebSync/grampswebsync.py:126 +msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." +#: GrampsWebSync/grampswebsync.py:128 +msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:348 -#, python-format -msgid "Encountered %s errors during download." +#: GrampsWebSync/grampswebsync.py:130 +msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +#: GrampsWebSync/grampswebsync.py:132 +msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:134 +msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 -msgid "Authentication failed. Please check your username and password." +#: GrampsWebSync/grampswebsync.py:137 +msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 -msgid "Access forbidden. Please check username and password." +#: GrampsWebSync/grampswebsync.py:140 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 -msgid "GrampsWeb service not found. Please check the URL." +#: GrampsWebSync/grampswebsync.py:142 +msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 -msgid "Too many requests, please try again in a few seconds." +#: GrampsWebSync/grampswebsync.py:144 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 -msgid "GrampsWeb tree is disabled." +#: GrampsWebSync/grampswebsync.py:146 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 +#: GrampsWebSync/grampswebsync.py:149 #, python-format msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:399 -msgid "Connection failed. Please check the URL and your internet connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:405 -msgid "Invalid server response. Please check the URL." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:409 +#: GrampsWebSync/grampswebsync.py:151 GrampsWebSync/grampswebsync.py:152 #, python-format msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:522 -msgid "Failed importing downloaded XML file." +#: GrampsWebSync/grampswebsync.py:187 +msgid "Introduction" msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:195 +msgid "Login" msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:201 GrampsWebSync/grampswebsync.py:223 +msgid "Progress Information" msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:206 +msgid "Final confirmation" msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:211 GrampsWebSync/grampswebsync.py:227 +msgid "Summary" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:216 +msgid "Media Files" msgstr "" -#: GrampsWebSync/grampswebsync.py:590 -#, python-format -msgid "Error %s while connecting to server." +#: GrampsWebSync/grampswebsync.py:289 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:292 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:296 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:377 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:379 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:384 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:385 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:435 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -199,101 +151,133 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:462 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:798 +#: GrampsWebSync/grampswebsync.py:471 msgid "Username: " msgstr "" -#: GrampsWebSync/grampswebsync.py:806 +#: GrampsWebSync/grampswebsync.py:479 msgid "Password: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:571 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:580 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:588 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 +#: GrampsWebSync/grampswebsync.py:596 msgid "Merge" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:622 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:949 GrampsWebSync/grampswebsync.py:954 +#: GrampsWebSync/grampswebsync.py:623 GrampsWebSync/grampswebsync.py:628 msgid "Added" msgstr "" -#: GrampsWebSync/grampswebsync.py:950 GrampsWebSync/grampswebsync.py:955 +#: GrampsWebSync/grampswebsync.py:624 GrampsWebSync/grampswebsync.py:629 msgid "Deleted" msgstr "" -#: GrampsWebSync/grampswebsync.py:951 GrampsWebSync/grampswebsync.py:956 -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:625 GrampsWebSync/grampswebsync.py:630 +#: GrampsWebSync/grampswebsync.py:632 msgid "Modified" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:627 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:632 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:689 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:715 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:721 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:723 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:727 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:732 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:761 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:764 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:804 #, python-format -msgid "Downloading %s media file(s)" -msgstr "" +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:819 #, python-format -msgid "Uploading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:857 +msgid "Media files are in sync." msgstr "" + +#: GrampsWebSync/grampswebsync.py:873 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:883 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:896 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" diff --git a/GrampsWebSync/session.py b/GrampsWebSync/session.py new file mode 100644 index 000000000..88a1e0f96 --- /dev/null +++ b/GrampsWebSync/session.py @@ -0,0 +1,625 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2021-2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Headless sync session for the Gramps Web Sync addon. + +:class:`SyncSession` runs a synchronization against a Gramps Web server, +progressing through the stages in :class:`State`. Callers drive it with +:meth:`~SyncSession.begin`, :meth:`~SyncSession.submit_credentials`, +:meth:`~SyncSession.confirm_changes` and :meth:`~SyncSession.confirm_files`, +and observe it through a :class:`SessionListener`. + +Collaborators are supplied as ports: :class:`Backend`, +:class:`CredentialStore`, :class:`MediaStore`, :class:`TaskRunner` and +:class:`Clock`. Failures are recorded as a :class:`SyncError` carrying an +:class:`ErrorKind`; callers are responsible for localizing them. +""" + +from __future__ import annotations + +import logging +from collections.abc import Callable +from dataclasses import dataclass +from enum import Enum, auto +from pathlib import Path +from typing import Any, Protocol +from urllib.error import HTTPError, URLError + +from const import MODE_BIDIRECTIONAL, Actions +from diffhandler import ( + WebApiSyncDiffHandler, + changes_to_actions, + has_local_actions, + has_remote_actions, +) +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import import_as_dict +from gramps.gen.errors import HandleError +from webapihandler import transaction_to_json + +LOG = logging.getLogger("grampswebsync") + +#: Transaction description recorded in both databases' undo history. +TXN_MSG = "Apply Gramps Web Sync changes" + +#: Server permission required to run a sync at all. +REQUIRED_PERMISSION = "ViewPrivate" + +#: Stages reported through :meth:`SessionListener.on_status`. +STATUS_FETCHING = "fetching" +STATUS_COMPARING = "comparing" +STATUS_LOCAL_APPLIED = "local_applied" + + +# ------------------------------------------------------------ +# +# States and errors +# +# ------------------------------------------------------------ +class State(Enum): + """The stages of a sync run.""" + + INTRO = auto() + LOGIN = auto() + COMPARING = auto() + REVIEW_CHANGES = auto() + APPLYING = auto() + REVIEW_FILES = auto() + TRANSFERRING = auto() + DONE = auto() + FAILED = auto() + + +class ErrorKind(Enum): + """Classification of a failure, independent of its localized wording.""" + + AUTH_FAILED = auto() # HTTP 401 + FORBIDDEN = auto() # HTTP 403 + NOT_FOUND = auto() # HTTP 404 + RATE_LIMITED = auto() # HTTP 429 + TREE_DISABLED = auto() # HTTP 503 + CONFLICT = auto() # HTTP 409 + SERVER_ERROR = auto() + CONNECTION_FAILED = auto() + INVALID_RESPONSE = auto() + INSUFFICIENT_PERMISSIONS = auto() + XML_IMPORT_FAILED = auto() + APPLY_FAILED = auto() + UNEXPECTED = auto() + + +@dataclass(frozen=True) +class SyncError: + """A failure recorded by the session. + + :param kind: The classification. + :param detail: Untranslated detail, e.g. an HTTP status or exception text. + """ + + kind: ErrorKind + detail: str = "" + + +class XmlImportFailed(Exception): + """The downloaded Gramps XML could not be imported.""" + + +class ApplyFailed(Exception): + """Applying the confirmed actions to the databases raised.""" + + +# Login and mid-sync failures read a few status codes differently. +_LOGIN_HTTP_ERRORS: dict[int, ErrorKind] = { + 401: ErrorKind.AUTH_FAILED, + 403: ErrorKind.FORBIDDEN, + 404: ErrorKind.NOT_FOUND, + 429: ErrorKind.RATE_LIMITED, + 503: ErrorKind.TREE_DISABLED, +} + +_SYNC_HTTP_ERRORS: dict[int, ErrorKind] = { + 401: ErrorKind.AUTH_FAILED, + 403: ErrorKind.FORBIDDEN, + 404: ErrorKind.NOT_FOUND, + 409: ErrorKind.CONFLICT, +} + + +def classify_http_error(exc: HTTPError, *, login: bool) -> SyncError: + """Classify an :class:`HTTPError` into a :class:`SyncError`. + + :param exc: The raised error. + :param login: Whether this happened while establishing the connection. + :returns: The corresponding :class:`SyncError`. + """ + table = _LOGIN_HTTP_ERRORS if login else _SYNC_HTTP_ERRORS + kind = table.get(exc.code, ErrorKind.SERVER_ERROR) + return SyncError(kind, str(exc.code)) + + +# ------------------------------------------------------------ +# +# Ports +# +# ------------------------------------------------------------ +class Backend(Protocol): + """What the session needs from a Gramps Web server. + + Implemented by :class:`webapihandler.WebApiHandler`. + """ + + def get_permissions(self) -> set[str]: ... + + def get_lang(self) -> str | None: ... + + def download_xml(self) -> Path: ... + + def commit( + self, + payload: list[dict[str, Any]], + force: bool = True, + progress_callback: Callable | None = None, + ) -> None: ... + + def get_missing_files(self) -> list[dict[str, Any]]: ... + + def download_media_file(self, handle: str, path: str) -> bool: ... + + def upload_media_file(self, handle: str, path: str) -> bool: ... + + +class CredentialStore(Protocol): + """Persistence for server credentials and the last-sync timestamp.""" + + def get_url(self) -> str: ... + + def get_username(self) -> str: ... + + def get_password(self) -> str | None: ... + + def get_timestamp(self) -> float: ... + + def set_timestamp(self, timestamp: float) -> None: ... + + def save_credentials(self, url: str, username: str, password: str) -> None: ... + + +class MediaStore(Protocol): + """Access to local media files belonging to the local database.""" + + def full_path(self, media: Any) -> str: ... + + def exists(self, media: Any) -> bool: ... + + +class TaskRunner(Protocol): + """Runs a potentially slow callable and reports the outcome back.""" + + def run( + self, + func: Callable[[], Any], + on_success: Callable[[Any], None], + on_error: Callable[[BaseException], None], + ) -> None: ... + + +class Clock(Protocol): + """Source of the current time.""" + + def now(self) -> float: ... + + +class SessionListener(Protocol): + """Receives session state changes, status and progress updates.""" + + def on_state_changed(self, state: State) -> None: ... + + def on_progress(self, kind: str, fraction: float) -> None: ... + + def on_status(self, stage: str) -> None: ... + + +# ------------------------------------------------------------ +# +# Transition table +# +# ------------------------------------------------------------ +def next_state(state: State, session: SyncSession) -> State: + """Return the state that follows ``state``. + + :param state: The state being left. + :param session: The session, consulted for the branch conditions. + :returns: The next state. + """ + if session.error is not None: + return State.FAILED + if state is State.INTRO: + return State.LOGIN + if state is State.LOGIN: + return State.COMPARING + if state is State.COMPARING: + if session.changes: + return State.REVIEW_CHANGES + return State.REVIEW_FILES if session.has_missing_files else State.DONE + if state is State.REVIEW_CHANGES: + return State.APPLYING + if state is State.APPLYING: + # Skipped entirely rather than shown empty: there is nothing to + # confirm when no file is missing on either side. + return State.REVIEW_FILES if session.has_missing_files else State.DONE + if state is State.REVIEW_FILES: + return State.TRANSFERRING if session.has_missing_files else State.DONE + if state is State.TRANSFERRING: + return State.DONE + return state + + +# ------------------------------------------------------------ +# +# SyncSession +# +# ------------------------------------------------------------ +class SyncSession: + """Drives one synchronization run against a Gramps Web server.""" + + def __init__( + self, + db, + user, + backend_factory: Callable[[str, str, str], Backend], + credentials: CredentialStore, + media: MediaStore, + runner: TaskRunner, + clock: Clock, + listener: SessionListener | None = None, + ) -> None: + """Initialize the session. + + :param db: The local (currently open) Gramps database. + :param user: A :class:`gramps.gen.user.User` for import/diff progress. + :param backend_factory: Builds a :class:`Backend` from url, username + and password. + :param credentials: Where credentials and the last-sync time live. + :param media: Access to local media files. + :param runner: Executes the slow steps. + :param clock: Supplies the time recorded as the last successful sync. + :param listener: Optional observer of state and progress. + """ + self.db1 = db + self.db2 = None + self._user = user + self._backend_factory = backend_factory + self.credentials = credentials + self.media = media + self.runner = runner + self.clock = clock + self.listener = listener + + self.state: State = State.INTRO + self.error: SyncError | None = None + #: Set when login fails. Recoverable, unlike :attr:`error`. + self.login_error: SyncError | None = None + + self.backend: Backend | None = None + self.sync: WebApiSyncDiffHandler | None = None + self.changes: Actions = [] + self.actions: Actions = [] + self.sync_mode: int = MODE_BIDIRECTIONAL + + self.missing_local: list[tuple[str, str]] = [] + self.missing_remote: list[tuple[str, str]] = [] + self.downloaded: dict[str, bool] = {} + self.uploaded: dict[str, bool] = {} + + self._closing = False + + # -------------------------------------------------------- + # Observable state + # -------------------------------------------------------- + @property + def has_missing_files(self) -> bool: + """Whether any media file is missing on either side.""" + return bool(self.missing_local or self.missing_remote) + + @property + def has_local_actions(self) -> bool: + """Whether the pending actions touch the local database.""" + return has_local_actions(self.actions) + + @property + def has_remote_actions(self) -> bool: + """Whether the pending actions touch the remote database.""" + return has_remote_actions(self.actions) + + # -------------------------------------------------------- + # Internals + # -------------------------------------------------------- + def _goto(self, state: State) -> None: + """Enter ``state`` and notify the listener.""" + LOG.debug("Sync session: %s -> %s", self.state.name, state.name) + self.state = state + if self.listener is not None: + self.listener.on_state_changed(state) + + def _advance(self) -> None: + """Move to whatever :func:`next_state` says comes next.""" + self._goto(next_state(self.state, self)) + + def _fail(self, error: SyncError) -> None: + """Record a terminal failure and move to :attr:`State.FAILED`.""" + LOG.warning("Sync failed: %s (%s)", error.kind.name, error.detail) + self.error = error + self._goto(State.FAILED) + + def _progress(self, kind: str, fraction: float) -> None: + """Forward a progress update to the listener, if any.""" + if self.listener is not None: + self.listener.on_progress(kind, fraction) + + def _status(self, stage: str) -> None: + """Forward a status update to the listener, if any.""" + if self.listener is not None: + self.listener.on_status(stage) + + def _classify(self, exc: BaseException, *, login: bool = False) -> SyncError: + """Turn an exception raised by a port into a :class:`SyncError`.""" + if isinstance(exc, XmlImportFailed): + return SyncError(ErrorKind.XML_IMPORT_FAILED) + if isinstance(exc, ApplyFailed): + return SyncError(ErrorKind.APPLY_FAILED, str(exc)) + if isinstance(exc, HTTPError): + return classify_http_error(exc, login=login) + if isinstance(exc, URLError): + return SyncError(ErrorKind.CONNECTION_FAILED, str(exc.reason)) + if isinstance(exc, ValueError): + kind = ErrorKind.INVALID_RESPONSE if login else ErrorKind.SERVER_ERROR + return SyncError(kind, str(exc)) + return SyncError(ErrorKind.UNEXPECTED, str(exc)) + + # -------------------------------------------------------- + # Intents + # -------------------------------------------------------- + def begin(self) -> None: + """Leave the introduction page.""" + self._advance() + + def submit_credentials(self, url: str, username: str, password: str) -> None: + """Connect, authenticate, then download and diff the remote tree. + + On an authentication or permission problem the session stays on + :attr:`State.LOGIN` with :attr:`login_error` set. + + :param url: Server URL, already sanitized by the caller. + :param username: Login name. + :param password: Password. + """ + self.login_error = None + self.credentials.save_credentials(url, username, password) + + try: + self.backend = self._backend_factory(url, username, password) + permissions = self.backend.get_permissions() + except Exception as exc: # noqa: BLE001 -- classified below + self.backend = None + self.login_error = self._classify(exc, login=True) + self._goto(State.LOGIN) + return + + if REQUIRED_PERMISSION not in permissions: + self.login_error = SyncError(ErrorKind.INSUFFICIENT_PERMISSIONS) + self._goto(State.LOGIN) + return + + self._goto(State.COMPARING) + self.runner.run(self._compare, self._on_compared, self._on_step_error) + + def confirm_changes(self, sync_mode: int) -> None: + """Accept the reviewed changes and apply them. + + :param sync_mode: One of the ``MODE_*`` constants from :mod:`const`. + """ + self.sync_mode = sync_mode + self._goto(State.APPLYING) + self.runner.run(self._apply, self._on_applied, self._on_step_error) + + def confirm_files(self) -> None: + """Accept the media file transfer and carry it out. + + Goes straight to :attr:`State.DONE` if nothing is missing. + """ + if not self.has_missing_files: + self._advance() + return + self._goto(State.TRANSFERRING) + self.runner.run(self._transfer, self._on_transferred, self._on_step_error) + + def cancel(self) -> None: + """Abandon the run and release the in-memory remote database.""" + self._closing = True + if self.db2 is not None: + self.db2.close() + self.db2 = None + self.sync = None # holds references to both databases + + # -------------------------------------------------------- + # Steps + # -------------------------------------------------------- + def _on_step_error(self, exc: BaseException) -> None: + """Handle an exception escaping one of the background steps.""" + self._fail(self._classify(exc)) + + def _compare(self) -> None: + """Download the remote tree and diff it against the local one.""" + if self._closing: + return + assert self.backend is not None + LOG.info("Downloading Gramps XML file.") + self._status(STATUS_FETCHING) + path = self.backend.download_xml() + LOG.debug("Downloaded XML to %s", path) + + db2 = import_as_dict(str(path), self._user) + path.unlink() + if db2 is None: + raise XmlImportFailed() + self.db2 = db2 + + LOG.info("Comparing local and remote data.") + self._status(STATUS_COMPARING) + last_synced = self.credentials.get_timestamp() or None + self.sync = WebApiSyncDiffHandler( + self.db1, self.db2, user=self._user, last_synced=last_synced + ) + self.changes = self.sync.get_changes() + + def _on_compared(self, _result: Any) -> None: + """Move on once the diff is available.""" + if self._closing: + return + if not self.changes: + LOG.info("Databases are in sync.") + self.credentials.set_timestamp(self.clock.now()) + self.missing_local = self._find_missing_local() + self.missing_remote = self._find_missing_remote() + self._advance() + + def _apply(self) -> None: + """Apply the confirmed actions locally, then push them to the server.""" + if self._closing: + return + assert self.backend is not None and self.sync is not None + self.actions = changes_to_actions(self.changes, self.sync_mode) + if not self.actions: + return + + LOG.info("Committing %s actions.", len(self.actions)) + try: + with DbTxn(TXN_MSG, self.sync.db1) as trans1: + with DbTxn(TXN_MSG, self.sync.db2) as trans2: + self.sync.commit_actions(self.actions, trans1, trans2) + lang = self.backend.get_lang() + payload = transaction_to_json(trans2, lang) + except Exception as exc: + raise ApplyFailed(str(exc)) from exc + + if self.has_local_actions: + self._status(STATUS_LOCAL_APPLIED) + + # Always force: the server compares against the XML-round-tripped + # object, which differs from the live one through serialization + # artifacts alone, yielding spurious 409s. + self.backend.commit( + payload, True, lambda fraction: self._progress("api", fraction) + ) + + def _on_applied(self, _result: Any) -> None: + """Record the sync time and collect media state.""" + if self._closing: + return + self.credentials.set_timestamp(self.clock.now()) + self.missing_local = self._find_missing_local() + self.missing_remote = self._find_missing_remote() + self._advance() + + def _transfer(self) -> None: + """Download media missing locally, then upload media missing remotely. + + Progress reporting lets the view pump the event loop, so the user can + cancel mid-transfer; both loops check for that between files. + """ + if self._closing: + return + assert self.backend is not None + for gramps_id, handle in self.missing_local: + if self._closing: + return + LOG.debug("Downloading file %s", gramps_id) + self.downloaded[gramps_id] = self._download_one(handle) + self._progress("download", len(self.downloaded) / len(self.missing_local)) + for gramps_id, handle in self.missing_remote: + if self._closing: + return + LOG.debug("Uploading file %s", gramps_id) + self.uploaded[gramps_id] = self._upload_one(handle) + self._progress("upload", len(self.uploaded) / len(self.missing_remote)) + + def _on_transferred(self, _result: Any) -> None: + """Finish the run.""" + if self._closing: + return + self._advance() + + # -------------------------------------------------------- + # Media helpers + # -------------------------------------------------------- + def _find_missing_local(self) -> list[tuple[str, str]]: + """Return ``(gramps_id, handle)`` for media whose file is absent locally.""" + return [ + (media.gramps_id, media.handle) + for media in self.db1.iter_media() + if not self.media.exists(media) + ] + + def _find_missing_remote(self) -> list[tuple[str, str]]: + """Return ``(gramps_id, handle)`` for media whose file is absent remotely.""" + assert self.backend is not None + return [ + (media["gramps_id"], media["handle"]) + for media in self.backend.get_missing_files() or [] + ] + + def _download_one(self, handle: str) -> bool: + """Download one media file, reporting failure rather than raising.""" + assert self.backend is not None + try: + obj = self.db1.get_media_from_handle(handle) + except HandleError: + LOG.warning("Cannot access media object %s", handle) + return False + try: + return self.backend.download_media_file(handle, self.media.full_path(obj)) + except Exception as exc: # noqa: BLE001 -- one bad file must not abort + LOG.warning("Failed to download media file %s: %s", obj.gramps_id, exc) + return False + + def _upload_one(self, handle: str) -> bool: + """Upload one media file. + + A file absent on both sides appears in both missing lists: the + download cannot supply it and there is nothing local to send, so it is + recorded as a failure instead of raising. + + :param handle: Handle of the media object to upload. + :returns: Whether the file was uploaded. + """ + assert self.backend is not None + try: + obj = self.db1.get_media_from_handle(handle) + except HandleError: + LOG.warning("Cannot access media object %s", handle) + return False + if not self.media.exists(obj): + LOG.warning( + "Cannot upload media file %s: missing locally as well (%s)", + obj.gramps_id, + self.media.full_path(obj), + ) + return False + return self.backend.upload_media_file(handle, self.media.full_path(obj)) + + diff --git a/GrampsWebSync/tests/__init__.py b/GrampsWebSync/tests/__init__.py new file mode 100644 index 000000000..52bcffda5 --- /dev/null +++ b/GrampsWebSync/tests/__init__.py @@ -0,0 +1,51 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Test package for the Gramps Web Sync addon. + +Importing this package pins GTK to 3.0, adds :data:`ADDON_DIR` and +:data:`ADDONS_ROOT` to ``sys.path`` and sets ``GRAMPS_RESOURCES`` if unset, so +test modules can import ``gramps`` and the addon's flat modules directly. +""" + +from __future__ import annotations + +import os +import sys + +import gi + +# Must precede any gramps import, or PyGObject may load GTK 4 and the +# gramps.gui chain dies on the GTK 3-only Gtk.IconSize.MENU. +gi.require_version("Gtk", "3.0") +gi.require_version("Gdk", "3.0") + +#: The ``GrampsWebSync`` addon directory, i.e. the parent of this package. +ADDON_DIR: str = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +#: The ``addons-source`` checkout root. +ADDONS_ROOT: str = os.path.dirname(ADDON_DIR) +if ADDONS_ROOT not in sys.path: + sys.path.insert(0, ADDONS_ROOT) + +if "GRAMPS_RESOURCES" not in os.environ: + import gramps + + os.environ["GRAMPS_RESOURCES"] = os.path.dirname(os.path.dirname(gramps.__file__)) diff --git a/GrampsWebSync/tests/fakes.py b/GrampsWebSync/tests/fakes.py new file mode 100644 index 000000000..9937c247b --- /dev/null +++ b/GrampsWebSync/tests/fakes.py @@ -0,0 +1,359 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""In-process test doubles for the :mod:`session` ports. + +:class:`FakeGrampsWebServer` implements :class:`session.Backend` over a real +Gramps database, exporting Gramps XML and applying transaction payloads; +:meth:`~FakeGrampsWebServer.fail_next` and +:meth:`~FakeGrampsWebServer.fail_always` inject faults. + +The remaining classes stand in for the other ports: +:class:`InlineTaskRunner`, :class:`FrozenClock`, +:class:`MemoryCredentialStore`, :class:`DirectoryMediaStore` and +:class:`RecordingListener`. None depend on a test framework. +""" + +from __future__ import annotations + +import os +import tempfile +from collections.abc import Callable +from pathlib import Path +from typing import Any +from urllib.error import HTTPError + +from gramps.cli.user import User +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import make_database +from gramps.gen.lib.json_utils import data_to_object +from gramps.plugins.export.exportxml import export_data + +#: Permissions a Gramps Web user needs for a sync to be allowed to proceed. +DEFAULT_PERMISSIONS = frozenset({"ViewPrivate", "EditObject", "AddObject"}) + + +def http_error(code: int, url: str = "https://example.org/api/") -> HTTPError: + """Build an :class:`HTTPError` with ``code``, for fault injection. + + :param code: The HTTP status to simulate. + :param url: The URL to attribute the error to. + :returns: A ready-to-raise :class:`HTTPError`. + """ + return HTTPError(url, code, f"Simulated HTTP {code}", {}, None) # type: ignore[arg-type] + + +# ------------------------------------------------------------ +# +# FakeGrampsWebServer +# +# ------------------------------------------------------------ +class FakeGrampsWebServer: + """A Gramps Web server backed by a real in-memory Gramps database. + + Satisfies the :class:`session.Backend` protocol. + + :param db: The database to serve. A fresh empty one is created if omitted. + :param permissions: Permissions to report for the logged-in user. + :param lang: Value returned by :meth:`get_lang`. + """ + + def __init__( + self, + db=None, + permissions: set[str] | frozenset[str] = DEFAULT_PERMISSIONS, + lang: str | None = "en", + ) -> None: + if db is None: + db = make_database("sqlite") + db.load(":memory:") + self.db = db + self.permissions = set(permissions) + self.lang = lang + self.user = User(auto_accept=True, quiet=True) + + #: Handles of media objects whose file the server actually holds. + self.media_files: dict[str, bytes] = {} + #: Every method call made against this server, in order. + self.calls: list[str] = [] + #: Each payload passed to :meth:`commit`. + self.committed: list[list[dict[str, Any]]] = [] + #: Method name -> exception, raised once then cleared. + self._fail_once: dict[str, BaseException] = {} + #: Method name -> exception, raised on every call. + self._fail_always: dict[str, BaseException] = {} + self._tempfiles: list[Path] = [] + + # -------------------------------------------------------- + # Fault injection + # -------------------------------------------------------- + def fail_next(self, method: str, exc: BaseException) -> None: + """Make the next call to ``method`` raise ``exc``. + + :param method: Name of the backend method, e.g. ``"download_xml"``. + :param exc: The exception to raise. + """ + self._fail_once[method] = exc + + def fail_always(self, method: str, exc: BaseException) -> None: + """Make every call to ``method`` raise ``exc``.""" + self._fail_always[method] = exc + + def _enter(self, method: str) -> None: + """Record a call and honour any fault configured for it.""" + self.calls.append(method) + exc = self._fail_once.pop(method, None) or self._fail_always.get(method) + if exc is not None: + raise exc + + # -------------------------------------------------------- + # Backend protocol + # -------------------------------------------------------- + def get_permissions(self) -> set[str]: + """Return the logged-in user's permissions.""" + self._enter("get_permissions") + return set(self.permissions) + + def get_lang(self) -> str | None: + """Return the server's configured language.""" + self._enter("get_lang") + return self.lang + + def download_xml(self) -> Path: + """Export the served database to a Gramps XML file. + + The caller owns the file and is expected to unlink it. + + :returns: Path to the exported ``.gramps`` file. + """ + self._enter("download_xml") + handle, name = tempfile.mkstemp(suffix=".gramps", prefix="fakeweb_") + os.close(handle) + path = Path(name) + self._tempfiles.append(path) + if not export_data(self.db, str(path), self.user): + raise ValueError("Fake server failed to export XML") + return path + + def commit( + self, + payload: list[dict[str, Any]], + force: bool = True, + progress_callback: Callable | None = None, + ) -> None: + """Apply a transaction payload to the served database. + + :param payload: Items as produced by + :func:`webapihandler.transaction_to_json`. + :param force: Accepted for protocol compatibility; ignored. + :param progress_callback: Called with a fraction in ``[0, 1]``. + """ + self._enter("commit") + self.committed.append(payload) + if not payload: + return + with DbTxn("Fake server transaction", self.db, batch=True) as trans: + for index, item in enumerate(payload): + self._apply_item(item, trans) + if progress_callback is not None: + progress_callback((index + 1) / len(payload)) + + def _apply_item(self, item: dict[str, Any], trans: DbTxn) -> None: + """Apply a single transaction item to the served database.""" + class_name = item["_class"] + if item["type"] == "delete": + method = self.db.method("remove_%s", class_name) + assert method is not None + method(item["handle"], trans) + return + obj = data_to_object(item["new"]) + # commit_* upserts, covering both "add" and "update". Passing the + # object's own change time keeps timestamps meaningful across a sync. + method = self.db.method("commit_%s", class_name) + assert method is not None + method(obj, trans, obj.change) + + def get_missing_files(self) -> list[dict[str, Any]]: + """Return media objects the server knows about but has no file for.""" + self._enter("get_missing_files") + return [ + {"gramps_id": media.gramps_id, "handle": media.handle} + for media in self.db.iter_media() + if media.handle not in self.media_files + ] + + def download_media_file(self, handle: str, path: str) -> bool: + """Write the server's copy of a media file to ``path``.""" + self._enter("download_media_file") + if handle not in self.media_files: + raise http_error(404) + Path(path).parent.mkdir(parents=True, exist_ok=True) + Path(path).write_bytes(self.media_files[handle]) + return True + + def upload_media_file(self, handle: str, path: str) -> bool: + """Store a media file uploaded by the client.""" + self._enter("upload_media_file") + self.media_files[handle] = Path(path).read_bytes() + return True + + # -------------------------------------------------------- + # Lifecycle + # -------------------------------------------------------- + def close(self) -> None: + """Close the served database and remove leftover export files.""" + for path in self._tempfiles: + path.unlink(missing_ok=True) + self._tempfiles.clear() + try: + self.db.close() + except Exception: # noqa: BLE001 -- teardown must not mask failures + pass + + +# ------------------------------------------------------------ +# +# Simple doubles +# +# ------------------------------------------------------------ +class InlineTaskRunner: + """Runs each task synchronously on the calling thread. + + By the time ``run`` returns, the step and its completion callback have + both finished. + """ + + def run( + self, + func: Callable[[], Any], + on_success: Callable[[Any], None], + on_error: Callable[[BaseException], None], + ) -> None: + """Execute ``func`` and dispatch to the appropriate callback.""" + try: + result = func() + except BaseException as exc: # noqa: BLE001 -- mirrors the real runner + on_error(exc) + else: + on_success(result) + + +class FrozenClock: + """A clock that only moves when :meth:`advance` is called. + + :param start: The initial time, as a POSIX timestamp. + """ + + def __init__(self, start: float = 1_700_000_000.0) -> None: + self.time = start + + def now(self) -> float: + """Return the current fake time.""" + return self.time + + def advance(self, seconds: float) -> None: + """Move the clock forward by ``seconds``.""" + self.time += seconds + + +class MemoryCredentialStore: + """In-memory stand-in for the config file and keyring. + + :param url: Initially stored server URL. + :param username: Initially stored user name. + :param password: Initially stored password. + :param timestamp: Initially stored last-sync time. + """ + + def __init__( + self, + url: str = "https://example.org/api", + username: str = "owner", + password: str = "secret", + timestamp: float = 0.0, + ) -> None: + self.url = url + self.username = username + self.password = password + self.timestamp = timestamp + #: Every ``(url, username, password)`` passed to + #: :meth:`save_credentials`. + self.saved: list[tuple[str, str, str]] = [] + + def get_url(self) -> str: + return self.url + + def get_username(self) -> str: + return self.username + + def get_password(self) -> str | None: + return self.password + + def get_timestamp(self) -> float: + return self.timestamp + + def set_timestamp(self, timestamp: float) -> None: + self.timestamp = timestamp + + def save_credentials(self, url: str, username: str, password: str) -> None: + # A changed URL invalidates the last-sync time, as in production. + if url != self.url: + self.timestamp = 0.0 + self.url = url + self.username = username + self.password = password + self.saved.append((url, username, password)) + + +class DirectoryMediaStore: + """Media store resolving paths under a directory owned by the test. + + :param base_dir: Directory that plays the role of the Gramps media path. + """ + + def __init__(self, base_dir: str) -> None: + self.base_dir = base_dir + + def full_path(self, media: Any) -> str: + """Return the absolute path of ``media``'s file.""" + return os.path.join(self.base_dir, media.get_path()) + + def exists(self, media: Any) -> bool: + """Whether ``media``'s file is present on disk.""" + return os.path.exists(self.full_path(media)) + + +class RecordingListener: + """Records state, status and progress updates for later assertions.""" + + def __init__(self) -> None: + #: States entered, in order. + self.states: list[Any] = [] + #: ``(kind, fraction)`` progress updates, in order. + self.progress: list[tuple[str, float]] = [] + #: Status stages reported, in order. + self.statuses: list[str] = [] + + def on_state_changed(self, state) -> None: + self.states.append(state) + + def on_progress(self, kind: str, fraction: float) -> None: + self.progress.append((kind, fraction)) + + def on_status(self, stage: str) -> None: + self.statuses.append(stage) diff --git a/GrampsWebSync/tests/scenario.py b/GrampsWebSync/tests/scenario.py new file mode 100644 index 000000000..bdf612ded --- /dev/null +++ b/GrampsWebSync/tests/scenario.py @@ -0,0 +1,401 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""A small DSL for writing Gramps Web Sync scenarios. + +:class:`SyncScenario` holds a local tree and a remote one derived from it. +Seed the local tree, call :meth:`~SyncScenario.share` to create the remote +side, edit either through its :class:`TreeEditor`, then +:meth:`~SyncScenario.run` a full sync and inspect the :class:`RunResult`:: + + with SyncScenario() as sc: + sc.seed_person("I0001", surname="Doe", changed_at=T0) + sc.share() + sc.local.edit_person("I0001", surname="Müller", changed_at=T2) + sc.remote.edit_person("I0001", surname="Mueller", changed_at=T3) + result = sc.run() + +:data:`T0` to :data:`T3` are increasing timestamps for the ``changed_at`` +argument every mutator takes. +""" + +from __future__ import annotations + +import os +import shutil +import tempfile +from dataclasses import dataclass +from typing import Any + +from const import MODE_BIDIRECTIONAL +from gramps.cli.user import User +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import import_as_dict, make_database +from gramps.gen.lib import Media, Name, Person, Surname, Tag +from session import State, SyncSession + +from .fakes import ( + DirectoryMediaStore, + FakeGrampsWebServer, + FrozenClock, + InlineTaskRunner, + MemoryCredentialStore, + RecordingListener, +) + +#: A convenient baseline "already synced" time for scenarios. +T0 = 1_600_000_000.0 +#: A time after :data:`T0`, for an edit on one side. +T1 = T0 + 1_000 +#: A time after :data:`T1`, for a later or competing edit. +T2 = T0 + 2_000 +#: A time after :data:`T2`. +T3 = T0 + 3_000 + + +class TreeEditor: + """Mutates one side of a scenario with explicit change timestamps. + + :param db: The database to edit. + :param media_dir: Directory holding this side's media files, if any. + """ + + def __init__(self, db, media_dir: str | None = None) -> None: + self.db = db + self.media_dir = media_dir + + # -------------------------------------------------------- + # Lookup + # -------------------------------------------------------- + def person(self, gramps_id: str) -> Person | None: + """Return the person with ``gramps_id``, or ``None`` if absent.""" + return self.db.get_person_from_gramps_id(gramps_id) + + def surname(self, gramps_id: str) -> str | None: + """Return the primary surname of ``gramps_id``, or ``None`` if absent.""" + person = self.person(gramps_id) + if person is None: + return None + return person.get_primary_name().get_surname() + + def tag(self, name: str) -> Tag | None: + """Return the tag called ``name``, or ``None`` if absent.""" + return self.db.get_tag_from_name(name) + + def person_ids(self) -> set[str]: + """Return every Gramps ID in this tree.""" + return { + self.db.get_person_from_handle(handle).gramps_id + for handle in self.db.get_person_handles() + } + + # -------------------------------------------------------- + # Mutation + # -------------------------------------------------------- + def add_person( + self, + gramps_id: str, + surname: str = "Doe", + first_name: str = "John", + changed_at: float = T0, + ) -> str: + """Add a person and return its handle. + + :param gramps_id: The Gramps ID to assign. + :param surname: Primary surname. + :param first_name: Given name. + :param changed_at: Value to record as the object's change time. + :returns: The new person's handle. + """ + person = Person() + person.set_gramps_id(gramps_id) + name = Name() + name.set_first_name(first_name) + surname_obj = Surname() + surname_obj.set_surname(surname) + name.add_surname(surname_obj) + person.set_primary_name(name) + with DbTxn(f"add {gramps_id}", self.db) as trans: + handle = self.db.add_person(person, trans) + self.db.commit_person(person, trans, changed_at) + return handle + + def edit_person( + self, + gramps_id: str, + surname: str | None = None, + first_name: str | None = None, + changed_at: float = T1, + ) -> None: + """Modify an existing person. + + :param gramps_id: Which person to edit. + :param surname: New primary surname, if given. + :param first_name: New given name, if given. + :param changed_at: Value to record as the object's change time. + :raises LookupError: If no such person exists. + """ + person = self.person(gramps_id) + if person is None: + raise LookupError(f"No person {gramps_id} in this tree") + name = person.get_primary_name() + if surname is not None: + surname_obj = Surname() + surname_obj.set_surname(surname) + name.set_surname_list([surname_obj]) + if first_name is not None: + name.set_first_name(first_name) + person.set_primary_name(name) + with DbTxn(f"edit {gramps_id}", self.db) as trans: + self.db.commit_person(person, trans, changed_at) + + def delete_person(self, gramps_id: str) -> None: + """Remove a person from this tree. + + :param gramps_id: Which person to remove. + :raises LookupError: If no such person exists. + """ + person = self.person(gramps_id) + if person is None: + raise LookupError(f"No person {gramps_id} in this tree") + with DbTxn(f"delete {gramps_id}", self.db) as trans: + self.db.remove_person(person.handle, trans) + + def add_media( + self, + gramps_id: str, + filename: str, + content: bytes = b"fake image bytes", + changed_at: float = T0, + on_disk: bool = True, + ) -> str: + """Add a media object, optionally writing its file. + + :param gramps_id: The Gramps ID to assign. + :param filename: Path relative to the media directory. + :param content: Bytes to write when ``on_disk`` is true. + :param changed_at: Value to record as the object's change time. + :param on_disk: Whether to create the file. ``False`` produces a media + object whose file is missing. + :returns: The new media object's handle. + """ + media = Media() + media.set_gramps_id(gramps_id) + media.set_path(filename) + media.set_description(gramps_id) + with DbTxn(f"add media {gramps_id}", self.db) as trans: + handle = self.db.add_media(media, trans) + self.db.commit_media(media, trans, changed_at) + if on_disk and self.media_dir is not None: + target = os.path.join(self.media_dir, filename) + os.makedirs(os.path.dirname(target), exist_ok=True) + with open(target, "wb") as fobj: + fobj.write(content) + return handle + + def add_tag(self, name: str, changed_at: float = T0) -> str: + """Add a tag and return its handle.""" + tag = Tag() + tag.set_name(name) + with DbTxn(f"add tag {name}", self.db) as trans: + handle = self.db.add_tag(tag, trans) + self.db.commit_tag(tag, trans, changed_at) + return handle + + +@dataclass +class RunResult: + """The outcome of a :meth:`SyncScenario.run`. + + :param states: Every state the session entered, in order. + :param progress: Progress updates as ``(kind, fraction)``. + :param statuses: Status stages reported, in order. + :param session: The session itself, for further assertions. + """ + + states: list[State] + progress: list[tuple[str, float]] + statuses: list[str] + session: SyncSession + + @property + def final_state(self) -> State: + """The state the session ended in.""" + return self.states[-1] if self.states else State.INTRO + + @property + def error(self): + """The terminal error, if the run failed.""" + return self.session.error + + @property + def login_error(self): + """The recoverable login error, if login was rejected.""" + return self.session.login_error + + def change_ids(self, change_type: str) -> set[str]: + """Return the Gramps IDs reported under a given change type. + + :param change_type: One of the ``C_*`` constants from :mod:`const`. + :returns: The set of Gramps IDs (tag *names*, for tags). + """ + ids = set() + for kind, _handle, class_name, obj1, obj2 in self.session.changes: + if kind != change_type: + continue + obj = obj1 if obj1 is not None else obj2 + ids.add(obj.name if class_name == "Tag" else obj.gramps_id) + return ids + + +class SyncScenario: + """Builds two related trees, then runs a full sync between them. + + Use as a context manager so the databases and temporary directories are + cleaned up:: + + with SyncScenario() as sc: + ... + """ + + def __init__(self, permissions: set[str] | None = None) -> None: + self._tmpdir = tempfile.mkdtemp(prefix="gws_scenario_") + self.local_media_dir = os.path.join(self._tmpdir, "local_media") + os.makedirs(self.local_media_dir, exist_ok=True) + + self.user = User(auto_accept=True, quiet=True) + self.db1 = make_database("sqlite") + self.db1.load(":memory:") + + self.local = TreeEditor(self.db1, media_dir=self.local_media_dir) + #: Set by :meth:`share`; until then there is no remote tree. + self.remote: TreeEditor | None = None + self.server: FakeGrampsWebServer | None = None + self._permissions = permissions + + self.clock = FrozenClock() + self.credentials = MemoryCredentialStore() + self.listener = RecordingListener() + + # -------------------------------------------------------- + # Setup + # -------------------------------------------------------- + def seed_person(self, gramps_id: str, **kwargs: Any) -> str: + """Add a person to the local tree before it is shared.""" + return self.local.add_person(gramps_id, **kwargs) + + def share(self, last_synced: float | None = T0) -> None: + """Create the remote tree as a copy of the local one. + + :param last_synced: Value to record as the last successful sync time. + Pass ``None`` to simulate a first-ever sync. + """ + export_path = os.path.join(self._tmpdir, "seed.gramps") + from gramps.plugins.export.exportxml import export_data + + if not export_data(self.db1, export_path, self.user): + raise RuntimeError("Failed to export the seed tree") + remote_db = import_as_dict(export_path, self.user) + if remote_db is None: + raise RuntimeError("Failed to import the seed tree") + + kwargs: dict[str, Any] = {"db": remote_db} + if self._permissions is not None: + kwargs["permissions"] = self._permissions + self.server = FakeGrampsWebServer(**kwargs) + self.remote = TreeEditor(remote_db) + self.credentials.timestamp = last_synced or 0.0 + + def _require_shared(self) -> FakeGrampsWebServer: + """Return the server, raising a clear error if :meth:`share` was skipped.""" + if self.server is None: + raise RuntimeError("Call share() before running the scenario") + return self.server + + def make_session(self) -> SyncSession: + """Build a :class:`SyncSession` wired to this scenario's fakes.""" + server = self._require_shared() + return SyncSession( + db=self.db1, + user=self.user, + backend_factory=lambda url, username, password: server, + credentials=self.credentials, + media=DirectoryMediaStore(self.local_media_dir), + runner=InlineTaskRunner(), + clock=self.clock, + listener=self.listener, + ) + + # -------------------------------------------------------- + # Running + # -------------------------------------------------------- + def run( + self, + mode: int = MODE_BIDIRECTIONAL, + confirm_files: bool = True, + url: str = "https://example.org/api", + username: str = "owner", + password: str = "secret", + ) -> RunResult: + """Drive a complete sync, answering every confirmation. + + Stops early if the session fails or returns to the login page. + + :param mode: The sync mode to confirm with. + :param confirm_files: Whether to accept the media transfer. ``False`` + leaves the session on :attr:`State.REVIEW_FILES`. + :param url: Server URL to submit. + :param username: User name to submit. + :param password: Password to submit. + :returns: A :class:`RunResult` describing the run. + """ + session = self.make_session() + session.begin() + session.submit_credentials(url, username, password) + + if session.state is State.REVIEW_CHANGES: + session.confirm_changes(mode) + if session.state is State.REVIEW_FILES and confirm_files: + session.confirm_files() + + return RunResult( + states=list(self.listener.states), + progress=list(self.listener.progress), + statuses=list(self.listener.statuses), + session=session, + ) + + # -------------------------------------------------------- + # Lifecycle + # -------------------------------------------------------- + def close(self) -> None: + """Release databases and temporary directories.""" + if self.server is not None: + self.server.close() + self.server = None + try: + self.db1.close() + except Exception: # noqa: BLE001 -- teardown must not mask failures + pass + shutil.rmtree(self._tmpdir, ignore_errors=True) + + def __enter__(self) -> SyncScenario: + return self + + def __exit__(self, *exc_info: Any) -> None: + self.close() diff --git a/GrampsWebSync/tests/test_adapters.py b/GrampsWebSync/tests/test_adapters.py new file mode 100644 index 000000000..4e938bb73 --- /dev/null +++ b/GrampsWebSync/tests/test_adapters.py @@ -0,0 +1,94 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Tests for the production ports in :mod:`adapters`. + +Drives a real :class:`GLib.MainLoop`; no widgets are built, so no display is +needed. +""" + +from __future__ import annotations + +import threading +import unittest + +from adapters import GLibTaskRunner +from gi.repository import GLib + +#: Milliseconds before an unresponsive loop is torn down. +TIMEOUT_MS = 5000 + + +def run_task(func): + """Run ``func`` through :class:`GLibTaskRunner` and return the outcome. + + :param func: The task to schedule. + :returns: Dict with ``result`` or ``error``, and ``thread``. + """ + outcome: dict = {} + loop = GLib.MainLoop() + + def on_success(result): + outcome["result"] = result + loop.quit() + + def on_error(exc): + outcome["error"] = exc + loop.quit() + + def wrapped(): + outcome["thread"] = threading.current_thread() + return func() + + GLibTaskRunner().run(wrapped, on_success, on_error) + GLib.timeout_add(TIMEOUT_MS, loop.quit) + loop.run() + return outcome + + +class GLibTaskRunnerTest(unittest.TestCase): + """The runner must keep work on the thread that owns GTK.""" + + def test_task_runs_on_the_calling_thread(self) -> None: + """Steps drive Gramps progress through the GUI ``User``, which touches + widgets. Running them on a worker thread segfaults inside ``diff_dbs``, + so the runner must not spawn one.""" + outcome = run_task(lambda: "done") + self.assertEqual(outcome.get("result"), "done") + self.assertIs(outcome["thread"], threading.current_thread()) + + def test_success_callback_receives_the_return_value(self) -> None: + self.assertEqual(run_task(lambda: 42).get("result"), 42) + + def test_failure_is_reported_to_the_error_callback(self) -> None: + def boom(): + raise ValueError("boom") + + outcome = run_task(boom) + self.assertNotIn("result", outcome) + self.assertIsInstance(outcome.get("error"), ValueError) + + def test_task_is_run_exactly_once(self) -> None: + """The idle source must remove itself, or it repeats forever.""" + calls = [] + run_task(lambda: calls.append(1)) + self.assertEqual(len(calls), 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_errors.py b/GrampsWebSync/tests/test_errors.py new file mode 100644 index 000000000..9beea27f5 --- /dev/null +++ b/GrampsWebSync/tests/test_errors.py @@ -0,0 +1,263 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Failure handling, exercised by injecting faults into the fake server.""" + +from __future__ import annotations + +import unittest +from urllib.error import URLError + +from session import ErrorKind, State + +from .fakes import http_error +from .scenario import T0, T2, SyncScenario + + +class LoginFailureTest(unittest.TestCase): + """Authentication and reachability problems at connect time.""" + + def make_scenario(self, **kwargs) -> SyncScenario: + scenario = SyncScenario(**kwargs) + self.addCleanup(scenario.close) + scenario.seed_person("I0001", surname="Doe", changed_at=T0) + scenario.share() + return scenario + + def test_http_statuses_map_to_distinct_error_kinds(self) -> None: + """Each status the server can return is reported as its own kind.""" + cases = [ + (401, ErrorKind.AUTH_FAILED), + (403, ErrorKind.FORBIDDEN), + (404, ErrorKind.NOT_FOUND), + (429, ErrorKind.RATE_LIMITED), + (503, ErrorKind.TREE_DISABLED), + (500, ErrorKind.SERVER_ERROR), + ] + for code, expected in cases: + with self.subTest(code=code): + scenario = self.make_scenario() + scenario.server.fail_always("get_permissions", http_error(code)) + result = scenario.run() + self.assertIs(result.final_state, State.LOGIN) + self.assertIsNotNone(result.login_error) + self.assertIs(result.login_error.kind, expected) + + def test_login_failure_is_recoverable_not_terminal(self) -> None: + """A rejected login must not set the terminal error.""" + scenario = self.make_scenario() + scenario.server.fail_always("get_permissions", http_error(401)) + result = scenario.run() + self.assertIsNone(result.error) + self.assertIs(result.final_state, State.LOGIN) + + def test_unreachable_server_reports_a_connection_failure(self) -> None: + scenario = self.make_scenario() + scenario.server.fail_always("get_permissions", URLError("no route to host")) + result = scenario.run() + self.assertIs(result.login_error.kind, ErrorKind.CONNECTION_FAILED) + + def test_non_api_response_reports_an_invalid_response(self) -> None: + """Something answered, but it was not the Gramps Web API.""" + scenario = self.make_scenario() + scenario.server.fail_always("get_permissions", ValueError("not JSON")) + result = scenario.run() + self.assertIs(result.login_error.kind, ErrorKind.INVALID_RESPONSE) + + def test_user_without_required_permission_is_refused(self) -> None: + """Without ViewPrivate the export is partial, so sync must not start.""" + scenario = self.make_scenario(permissions={"ViewObject"}) + result = scenario.run() + self.assertIs(result.final_state, State.LOGIN) + self.assertIs(result.login_error.kind, ErrorKind.INSUFFICIENT_PERMISSIONS) + + def test_failed_login_does_not_touch_either_tree(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.server.fail_always("get_permissions", http_error(401)) + + scenario.run() + + self.assertEqual(scenario.remote.surname("I0001"), "Doe") + self.assertEqual(scenario.server.committed, []) + + +class MidSyncFailureTest(unittest.TestCase): + """Failures after the connection is established are terminal.""" + + def make_scenario(self) -> SyncScenario: + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.seed_person("I0001", surname="Doe", changed_at=T0) + scenario.seed_person("I0002", surname="Roe", changed_at=T0) + scenario.share() + return scenario + + def test_export_download_failure_fails_the_run(self) -> None: + scenario = self.make_scenario() + scenario.server.fail_always("download_xml", http_error(500)) + result = scenario.run() + self.assertIs(result.final_state, State.FAILED) + self.assertIs(result.error.kind, ErrorKind.SERVER_ERROR) + + def test_transaction_conflict_is_reported_as_a_conflict(self) -> None: + """HTTP 409 means the server rejected the transaction as stale.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.server.fail_always("commit", http_error(409)) + + result = scenario.run() + + self.assertIs(result.final_state, State.FAILED) + self.assertIs(result.error.kind, ErrorKind.CONFLICT) + + def test_expired_token_mid_sync_is_reported_as_auth_failure(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.server.fail_always("commit", http_error(401)) + result = scenario.run() + self.assertIs(result.error.kind, ErrorKind.AUTH_FAILED) + + def test_failed_run_does_not_record_a_sync_timestamp(self) -> None: + """Recording it would move the diff cutoff past the unsynced changes.""" + scenario = self.make_scenario() + scenario.credentials.timestamp = T0 + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.server.fail_always("commit", http_error(409)) + + scenario.run() + + self.assertEqual(scenario.credentials.get_timestamp(), T0) + + def test_local_changes_survive_a_failed_remote_commit(self) -> None: + """The local transaction commits before the upload is attempted.""" + scenario = self.make_scenario() + scenario.remote.edit_person("I0002", surname="Neu", changed_at=T2) + scenario.server.fail_always("commit", http_error(500)) + + result = scenario.run() + + self.assertIs(result.final_state, State.FAILED) + self.assertEqual(scenario.local.surname("I0002"), "Neu") + + +class CancellationTest(unittest.TestCase): + """Cancelling must stop work that has been scheduled but not yet run.""" + + def make_scenario(self) -> SyncScenario: + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.seed_person("I0001", surname="Doe", changed_at=T0) + scenario.share() + return scenario + + def test_cancel_before_compare_skips_the_download(self) -> None: + scenario = self.make_scenario() + session = scenario.make_session() + session.begin() + session.cancel() + + session._compare() + + self.assertNotIn("download_xml", scenario.server.calls) + + def test_cancel_before_apply_sends_nothing(self) -> None: + """``cancel`` releases the diff handler, so applying must not proceed.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + session = scenario.make_session() + session.begin() + session.submit_credentials("https://example.org/api", "owner", "secret") + session.cancel() + + session._apply() + + self.assertEqual(scenario.server.committed, []) + + def test_cancel_before_transfer_moves_no_files(self) -> None: + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.local.add_media("O0001", "photo.jpg", changed_at=T0) + scenario.share() + session = scenario.make_session() + session.begin() + session.submit_credentials("https://example.org/api", "owner", "secret") + session.cancel() + + session._transfer() + + self.assertEqual(scenario.server.media_files, {}) + + +class MediaFailureTest(unittest.TestCase): + """One bad media file must not abort the whole transfer.""" + + def make_scenario(self) -> SyncScenario: + scenario = SyncScenario() + self.addCleanup(scenario.close) + return scenario + + def test_failed_download_is_recorded_without_failing_the_run(self) -> None: + """A single unreadable file is recorded and the run continues.""" + scenario = self.make_scenario() + first = scenario.local.add_media("O0001", "first.jpg", on_disk=False) + second = scenario.local.add_media("O0002", "second.jpg", on_disk=False) + scenario.share() + # The server holds both files, so nothing needs uploading and this + # test isolates the download path. + scenario.server.media_files[first] = b"first bytes" + scenario.server.media_files[second] = b"second bytes" + scenario.server.fail_next("download_media_file", http_error(500)) + + result = scenario.run() + + self.assertIs(result.final_state, State.DONE) + self.assertEqual(result.session.downloaded, {"O0001": False, "O0002": True}) + + def test_file_missing_on_both_sides_is_recorded_not_fatal(self) -> None: + """Such an object is in both missing lists; neither side can supply it. + + The download 404s and the upload has nothing to send, so both are + recorded as failures and the run still completes. + """ + scenario = self.make_scenario() + scenario.local.add_media("O0001", "nowhere.jpg", on_disk=False) + scenario.share() + + result = scenario.run() + + self.assertIs(result.final_state, State.DONE) + self.assertIsNone(result.error) + self.assertEqual(result.session.downloaded, {"O0001": False}) + self.assertEqual(result.session.uploaded, {"O0001": False}) + + def test_upload_still_fails_the_run_on_a_server_error(self) -> None: + """The new guard must not swallow genuine transport failures.""" + scenario = self.make_scenario() + scenario.local.add_media("O0002", "present.jpg", changed_at=T0) + scenario.share() + scenario.server.fail_always("upload_media_file", http_error(500)) + + result = scenario.run() + + self.assertIs(result.final_state, State.FAILED) + self.assertIs(result.error.kind, ErrorKind.SERVER_ERROR) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_sync_flow.py b/GrampsWebSync/tests/test_sync_flow.py new file mode 100644 index 000000000..60b6f2fd6 --- /dev/null +++ b/GrampsWebSync/tests/test_sync_flow.py @@ -0,0 +1,320 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""End-to-end sync runs against an in-process fake Gramps Web server.""" + +from __future__ import annotations + +import os +import unittest + +from const import ( + C_ADD_LOC, + C_ADD_REM, + C_DEL_LOC, + C_DEL_REM, + C_UPD_BOTH, + C_UPD_LOC, + C_UPD_REM, + MODE_BIDIRECTIONAL, + MODE_MERGE, + MODE_RESET_TO_LOCAL, + MODE_RESET_TO_REMOTE, +) +from session import ( + STATUS_COMPARING, + STATUS_FETCHING, + STATUS_LOCAL_APPLIED, + State, +) + +from .scenario import T0, T2, T3, SyncScenario + + +class SyncFlowTestCase(unittest.TestCase): + """Base class providing a seeded, shared two-tree scenario.""" + + def make_scenario(self, **kwargs) -> SyncScenario: + """Return a shared scenario with two people, registered for teardown.""" + scenario = SyncScenario(**kwargs) + self.addCleanup(scenario.close) + scenario.seed_person("I0001", surname="Doe", changed_at=T0) + scenario.seed_person("I0002", surname="Roe", changed_at=T0) + scenario.share() + return scenario + + +class InSyncTest(SyncFlowTestCase): + """Two identical trees.""" + + def test_identical_trees_report_no_changes(self) -> None: + """A tree exported and reimported must diff as unchanged.""" + scenario = self.make_scenario() + result = scenario.run() + self.assertEqual(result.session.changes, []) + self.assertIs(result.final_state, State.DONE) + + def test_confirmation_stage_is_skipped(self) -> None: + """With nothing to confirm, the flow bypasses the review page.""" + scenario = self.make_scenario() + result = scenario.run() + self.assertNotIn(State.REVIEW_CHANGES, result.states) + self.assertNotIn(State.APPLYING, result.states) + + def test_nothing_is_sent_to_the_server(self) -> None: + """An in-sync run must not post a transaction.""" + scenario = self.make_scenario() + scenario.run() + self.assertEqual(scenario.server.committed, []) + + def test_media_confirmation_is_skipped_when_nothing_is_missing(self) -> None: + """No page may be shown with two empty lists and an Apply button.""" + scenario = self.make_scenario() + result = scenario.run() + self.assertNotIn(State.REVIEW_FILES, result.states) + self.assertEqual( + result.states, [State.LOGIN, State.COMPARING, State.DONE] + ) + + +class BidirectionalSyncTest(SyncFlowTestCase): + """Changes made on one side only, propagating to the other.""" + + def test_local_edit_reaches_the_server(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + result = scenario.run() + self.assertEqual(result.change_ids(C_UPD_LOC), {"I0001"}) + self.assertEqual(scenario.remote.surname("I0001"), "Müller") + + def test_remote_edit_reaches_the_local_tree(self) -> None: + scenario = self.make_scenario() + scenario.remote.edit_person("I0001", surname="Mueller", changed_at=T2) + result = scenario.run() + self.assertEqual(result.change_ids(C_UPD_REM), {"I0001"}) + self.assertEqual(scenario.local.surname("I0001"), "Mueller") + + def test_local_addition_reaches_the_server(self) -> None: + scenario = self.make_scenario() + scenario.local.add_person("I9001", surname="Neu", changed_at=T2) + result = scenario.run() + self.assertEqual(result.change_ids(C_ADD_LOC), {"I9001"}) + self.assertIn("I9001", scenario.remote.person_ids()) + + def test_remote_addition_reaches_the_local_tree(self) -> None: + scenario = self.make_scenario() + scenario.remote.add_person("I9002", surname="Neuer", changed_at=T2) + result = scenario.run() + self.assertEqual(result.change_ids(C_ADD_REM), {"I9002"}) + self.assertIn("I9002", scenario.local.person_ids()) + + def test_local_deletion_reaches_the_server(self) -> None: + scenario = self.make_scenario() + scenario.local.delete_person("I0002") + result = scenario.run() + self.assertEqual(result.change_ids(C_DEL_LOC), {"I0002"}) + self.assertNotIn("I0002", scenario.remote.person_ids()) + + def test_remote_deletion_reaches_the_local_tree(self) -> None: + scenario = self.make_scenario() + scenario.remote.delete_person("I0002") + result = scenario.run() + self.assertEqual(result.change_ids(C_DEL_REM), {"I0002"}) + self.assertNotIn("I0002", scenario.local.person_ids()) + + def test_edits_on_both_sides_are_flagged_as_simultaneous(self) -> None: + """Competing edits to one object are reported as simultaneous.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.remote.edit_person("I0001", surname="Mueller", changed_at=T3) + result = scenario.run() + self.assertEqual(result.change_ids(C_UPD_BOTH), {"I0001"}) + + def test_independent_changes_on_both_sides_both_propagate(self) -> None: + """Each side's change lands on the other in a single run.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.remote.add_person("I9003", surname="Neu", changed_at=T2) + scenario.run() + self.assertEqual(scenario.remote.surname("I0001"), "Müller") + self.assertIn("I9003", scenario.local.person_ids()) + + +class SyncModeTest(SyncFlowTestCase): + """The four sync modes resolve the same divergence differently.""" + + def diverged(self) -> SyncScenario: + """Return a scenario where each side added a distinct person.""" + scenario = self.make_scenario() + scenario.local.add_person("I9100", surname="LocalOnly", changed_at=T2) + scenario.remote.add_person("I9200", surname="RemoteOnly", changed_at=T2) + return scenario + + def test_bidirectional_keeps_both_additions(self) -> None: + scenario = self.diverged() + scenario.run(mode=MODE_BIDIRECTIONAL) + for side in (scenario.local, scenario.remote): + self.assertIn("I9100", side.person_ids()) + self.assertIn("I9200", side.person_ids()) + + def test_reset_to_local_makes_the_server_match_the_local_tree(self) -> None: + scenario = self.diverged() + scenario.run(mode=MODE_RESET_TO_LOCAL) + self.assertIn("I9100", scenario.remote.person_ids()) + self.assertNotIn("I9200", scenario.remote.person_ids()) + self.assertNotIn("I9200", scenario.local.person_ids()) + + def test_reset_to_remote_makes_the_local_tree_match_the_server(self) -> None: + scenario = self.diverged() + scenario.run(mode=MODE_RESET_TO_REMOTE) + self.assertIn("I9200", scenario.local.person_ids()) + self.assertNotIn("I9100", scenario.local.person_ids()) + self.assertNotIn("I9100", scenario.remote.person_ids()) + + def test_merge_restores_a_locally_deleted_object(self) -> None: + """Merge mode never deletes; a removal on one side is undone.""" + scenario = self.make_scenario() + scenario.local.delete_person("I0002") + scenario.run(mode=MODE_MERGE) + self.assertIn("I0002", scenario.local.person_ids()) + self.assertIn("I0002", scenario.remote.person_ids()) + + +class MediaFileTest(SyncFlowTestCase): + """Media files, which sync separately from object data.""" + + def test_file_missing_locally_is_downloaded(self) -> None: + scenario = SyncScenario() + self.addCleanup(scenario.close) + handle = scenario.local.add_media( + "O0001", "photo.jpg", changed_at=T0, on_disk=False + ) + scenario.share() + scenario.server.media_files[handle] = b"server image bytes" + + result = scenario.run() + + self.assertIs(result.final_state, State.DONE) + self.assertEqual(result.session.downloaded, {"O0001": True}) + media = scenario.db1.get_media_from_handle(handle) + local_path = os.path.join(scenario.local_media_dir, media.get_path()) + self.assertTrue(os.path.exists(local_path)) + self.assertEqual(open(local_path, "rb").read(), b"server image bytes") + + def test_file_missing_remotely_is_uploaded(self) -> None: + scenario = SyncScenario() + self.addCleanup(scenario.close) + handle = scenario.local.add_media( + "O0002", "portrait.jpg", content=b"local bytes", changed_at=T0 + ) + scenario.share() + self.assertNotIn(handle, scenario.server.media_files) + + result = scenario.run() + + self.assertEqual(result.session.uploaded, {"O0002": True}) + self.assertEqual(scenario.server.media_files[handle], b"local bytes") + + def test_transfer_stage_is_skipped_when_all_files_are_present(self) -> None: + scenario = SyncScenario() + self.addCleanup(scenario.close) + handle = scenario.local.add_media("O0003", "ok.jpg", changed_at=T0) + scenario.share() + scenario.server.media_files[handle] = b"fake image bytes" + + result = scenario.run() + + self.assertNotIn(State.TRANSFERRING, result.states) + self.assertIs(result.final_state, State.DONE) + + def test_declining_the_transfer_leaves_the_session_on_review(self) -> None: + """Declining the transfer must neither advance nor fail.""" + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.local.add_media("O0004", "skipped.jpg", changed_at=T0) + scenario.share() + + result = scenario.run(confirm_files=False) + + self.assertIs(result.final_state, State.REVIEW_FILES) + self.assertEqual(scenario.server.media_files, {}) + + +class TimestampTest(SyncFlowTestCase): + """The last-sync timestamp, which drives every later diff.""" + + def test_successful_run_records_the_sync_time(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + scenario.clock.time = 1_700_000_500.0 + + scenario.run() + + self.assertEqual(scenario.credentials.get_timestamp(), 1_700_000_500.0) + + def test_in_sync_run_also_records_the_sync_time(self) -> None: + """Finding no differences still counts as a successful sync.""" + scenario = self.make_scenario() + scenario.clock.time = 1_700_000_900.0 + + scenario.run() + + self.assertEqual(scenario.credentials.get_timestamp(), 1_700_000_900.0) + + def test_changing_the_url_clears_the_stored_timestamp(self) -> None: + """A timestamp is meaningless against a different tree.""" + scenario = self.make_scenario() + scenario.credentials.timestamp = T3 + scenario.run(url="https://elsewhere.example/api") + self.assertNotEqual(scenario.credentials.get_timestamp(), T3) + + +class ProgressTest(SyncFlowTestCase): + """Progress and status reporting reach the listener.""" + + def test_applying_changes_reports_api_progress(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + result = scenario.run() + api_progress = [f for kind, f in result.progress if kind == "api"] + self.assertTrue(api_progress) + self.assertEqual(api_progress[-1], 1.0) + + def test_comparison_reports_fetching_then_comparing(self) -> None: + scenario = self.make_scenario() + result = scenario.run() + self.assertEqual( + [s for s in result.statuses if s in (STATUS_FETCHING, STATUS_COMPARING)], + [STATUS_FETCHING, STATUS_COMPARING], + ) + + def test_local_commit_is_reported(self) -> None: + scenario = self.make_scenario() + scenario.remote.edit_person("I0001", surname="Mueller", changed_at=T2) + result = scenario.run() + self.assertIn(STATUS_LOCAL_APPLIED, result.statuses) + + def test_remote_only_changes_do_not_report_a_local_commit(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Müller", changed_at=T2) + result = scenario.run() + self.assertNotIn(STATUS_LOCAL_APPLIED, result.statuses) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_transitions.py b/GrampsWebSync/tests/test_transitions.py new file mode 100644 index 000000000..2ef4cf94f --- /dev/null +++ b/GrampsWebSync/tests/test_transitions.py @@ -0,0 +1,129 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Unit tests for :func:`session.next_state`.""" + +from __future__ import annotations + +import unittest +from types import SimpleNamespace + +from session import ErrorKind, State, SyncError, next_state + + +def fake_session( + error: SyncError | None = None, + changes: list | None = None, + has_missing_files: bool = False, +) -> SimpleNamespace: + """Build a stand-in exposing the attributes :func:`next_state` reads. + + :param error: A terminal error, if any. + :param changes: The pending change list. + :param has_missing_files: Whether media files are missing on either side. + :returns: The stand-in session. + """ + return SimpleNamespace( + error=error, + changes=changes if changes is not None else [], + has_missing_files=has_missing_files, + ) + + +class NextStateTest(unittest.TestCase): + """The happy-path chain and its two conditional skips.""" + + def test_linear_path_with_changes_and_files(self) -> None: + """With both changes and missing files, every state is visited.""" + session = fake_session(changes=["a change"], has_missing_files=True) + expected = [ + (State.INTRO, State.LOGIN), + (State.LOGIN, State.COMPARING), + (State.COMPARING, State.REVIEW_CHANGES), + (State.REVIEW_CHANGES, State.APPLYING), + (State.APPLYING, State.REVIEW_FILES), + (State.REVIEW_FILES, State.TRANSFERRING), + (State.TRANSFERRING, State.DONE), + ] + for state, following in expected: + with self.subTest(state=state.name): + self.assertIs(next_state(state, session), following) + + def test_no_changes_but_missing_files_goes_to_the_media_stage(self) -> None: + session = fake_session(changes=[], has_missing_files=True) + self.assertIs(next_state(State.COMPARING, session), State.REVIEW_FILES) + + def test_nothing_to_do_at_all_ends_the_run(self) -> None: + """Fully in sync: no confirmation page of any kind is shown.""" + session = fake_session(changes=[], has_missing_files=False) + self.assertIs(next_state(State.COMPARING, session), State.DONE) + + def test_applying_with_no_missing_files_ends_the_run(self) -> None: + """The media page is skipped rather than shown with empty lists.""" + session = fake_session(changes=["a change"], has_missing_files=False) + self.assertIs(next_state(State.APPLYING, session), State.DONE) + + def test_changes_present_requires_confirmation(self) -> None: + """Any pending change must be confirmed before it is applied.""" + session = fake_session(changes=["a change"]) + self.assertIs(next_state(State.COMPARING, session), State.REVIEW_CHANGES) + + def test_no_missing_files_skips_transfer(self) -> None: + """With all media present on both sides, the run ends after review.""" + session = fake_session(has_missing_files=False) + self.assertIs(next_state(State.REVIEW_FILES, session), State.DONE) + + def test_missing_files_requires_transfer(self) -> None: + """Missing media on either side means a transfer stage.""" + session = fake_session(has_missing_files=True) + self.assertIs(next_state(State.REVIEW_FILES, session), State.TRANSFERRING) + + +class ErrorShortCircuitTest(unittest.TestCase): + """A recorded error overrides the flow from wherever it happened.""" + + def test_error_from_any_state_goes_to_failed(self) -> None: + """Every non-terminal state jumps to FAILED once an error is set.""" + session = fake_session( + error=SyncError(ErrorKind.CONFLICT), changes=["a change"] + ) + for state in State: + if state in (State.DONE, State.FAILED): + continue + with self.subTest(state=state.name): + self.assertIs(next_state(state, session), State.FAILED) + + def test_error_outranks_the_skip_conditions(self) -> None: + """The error check runs before any branch that might route elsewhere.""" + session = fake_session(error=SyncError(ErrorKind.AUTH_FAILED), changes=[]) + self.assertIs(next_state(State.COMPARING, session), State.FAILED) + + +class TerminalStateTest(unittest.TestCase): + """Terminal states do not advance on their own.""" + + def test_done_is_terminal(self) -> None: + self.assertIs(next_state(State.DONE, fake_session()), State.DONE) + + def test_failed_is_terminal(self) -> None: + session = fake_session(error=SyncError(ErrorKind.UNEXPECTED)) + self.assertIs(next_state(State.FAILED, session), State.FAILED) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_view_mapping.py b/GrampsWebSync/tests/test_view_mapping.py new file mode 100644 index 000000000..9d6ed607b --- /dev/null +++ b/GrampsWebSync/tests/test_view_mapping.py @@ -0,0 +1,72 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Checks that the GTK view's lookup tables cover the session's enums. + +Constructs no widgets, so no display is needed. +""" + +from __future__ import annotations + +import unittest + +import grampswebsync +from grampswebsync import PAGE_FOR_STATE, error_message +from session import ErrorKind, State + + +class PageMappingTest(unittest.TestCase): + """Every flow state must correspond to a page the assistant owns.""" + + def test_every_state_maps_to_a_page(self) -> None: + self.assertEqual(set(PAGE_FOR_STATE), set(State)) + + def test_page_indices_match_the_assistant_page_order(self) -> None: + """Indices must be the contiguous range the pages are appended in. + + The assistant addresses pages positionally, so a gap or an off-by-one + here sends the user to the wrong page rather than failing loudly. + """ + self.assertEqual(sorted(set(PAGE_FOR_STATE.values())), list(range(8))) + + def test_terminal_states_share_the_conclusion_page(self) -> None: + """Success and failure are both reported on the last page.""" + self.assertEqual( + PAGE_FOR_STATE[State.DONE], PAGE_FOR_STATE[State.FAILED] + ) + self.assertEqual(PAGE_FOR_STATE[State.DONE], grampswebsync.PAGE_CONCLUSION) + + +class ErrorMessageTest(unittest.TestCase): + """Every error the session can record must render as something readable.""" + + def test_every_error_kind_has_a_message(self) -> None: + """An unmapped kind would surface to the user as an empty dialog.""" + for kind in ErrorKind: + with self.subTest(kind=kind.name): + message = error_message(kind, "42") + self.assertTrue(message.strip(), f"{kind.name} rendered empty") + + def test_detail_is_included_where_it_carries_information(self) -> None: + """Status codes must reach the user for the otherwise-opaque kinds.""" + self.assertIn("42", error_message(ErrorKind.SERVER_ERROR, "42")) + self.assertIn("boom", error_message(ErrorKind.UNEXPECTED, "boom")) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/webapihandler.py b/GrampsWebSync/webapihandler.py index 85959a64e..5f5414730 100644 --- a/GrampsWebSync/webapihandler.py +++ b/GrampsWebSync/webapihandler.py @@ -204,7 +204,7 @@ def download_xml(self) -> Path: def commit( self, - payload: dict[str, Any], + payload: list[dict[str, Any]], force: bool = True, progress_callback: Callable | None = None, ) -> None: From f77addf4bcbe2eb766993c6af3326b96b64ef4f5 Mon Sep 17 00:00:00 2001 From: David Straub Date: Mon, 27 Jul 2026 16:50:22 +0200 Subject: [PATCH 069/156] Address copilot review comments --- GrampsWebSync/grampswebsync.py | 5 +++-- GrampsWebSync/session.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/GrampsWebSync/grampswebsync.py b/GrampsWebSync/grampswebsync.py index 918d586cd..9b91d4082 100644 --- a/GrampsWebSync/grampswebsync.py +++ b/GrampsWebSync/grampswebsync.py @@ -30,7 +30,7 @@ from __future__ import annotations import logging -from urllib.parse import urlparse +from urllib.parse import urlparse, urlunparse from adapters import ( ConfigCredentialStore, @@ -377,6 +377,7 @@ def sanitize_url(self, url: str) -> str: :param url: The URL as typed by the user. :returns: The URL to actually use. """ + url = url.strip() parsed_url = urlparse(url) if parsed_url.scheme == "": # if no httpX given, prepend https! @@ -395,7 +396,7 @@ def sanitize_url(self, url: str) -> str: parent=self.window, ) if not question.run(): - return url.replace("http", "https") + return urlunparse(parsed_url._replace(scheme="https")) return url diff --git a/GrampsWebSync/session.py b/GrampsWebSync/session.py index 88a1e0f96..83dd8c87a 100644 --- a/GrampsWebSync/session.py +++ b/GrampsWebSync/session.py @@ -473,8 +473,10 @@ def _compare(self) -> None: path = self.backend.download_xml() LOG.debug("Downloaded XML to %s", path) - db2 = import_as_dict(str(path), self._user) - path.unlink() + try: + db2 = import_as_dict(str(path), self._user) + finally: + path.unlink() if db2 is None: raise XmlImportFailed() self.db2 = db2 From 5cb98909c2336fb35f47913fdb7b2a62f28a20bc Mon Sep 17 00:00:00 2001 From: David Straub Date: Mon, 27 Jul 2026 19:42:55 +0200 Subject: [PATCH 070/156] Remove GTK pin --- GrampsWebSync/tests/__init__.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/GrampsWebSync/tests/__init__.py b/GrampsWebSync/tests/__init__.py index 52bcffda5..ddadbf783 100644 --- a/GrampsWebSync/tests/__init__.py +++ b/GrampsWebSync/tests/__init__.py @@ -18,9 +18,10 @@ """Test package for the Gramps Web Sync addon. -Importing this package pins GTK to 3.0, adds :data:`ADDON_DIR` and -:data:`ADDONS_ROOT` to ``sys.path`` and sets ``GRAMPS_RESOURCES`` if unset, so -test modules can import ``gramps`` and the addon's flat modules directly. +Importing this package adds :data:`ADDON_DIR` and :data:`ADDONS_ROOT` to +``sys.path`` and sets ``GRAMPS_RESOURCES`` if unset, so test modules can +import ``gramps`` and the addon's flat modules directly. GTK/Gdk version +pinning is handled repo-wide by the root ``tests/__init__.py`` (PR #950). """ from __future__ import annotations @@ -28,13 +29,6 @@ import os import sys -import gi - -# Must precede any gramps import, or PyGObject may load GTK 4 and the -# gramps.gui chain dies on the GTK 3-only Gtk.IconSize.MENU. -gi.require_version("Gtk", "3.0") -gi.require_version("Gdk", "3.0") - #: The ``GrampsWebSync`` addon directory, i.e. the parent of this package. ADDON_DIR: str = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if ADDON_DIR not in sys.path: From c72cd3a5e2ece30495d2ddf2c6aa4253a3b6d200 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Fri, 31 Jul 2026 09:29:58 -0700 Subject: [PATCH 071/156] Merge Gramps Web Sync refactor rebased onto gramps61-#1003 --- GrampsWebSync/grampswebsync.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GrampsWebSync/grampswebsync.gpr.py b/GrampsWebSync/grampswebsync.gpr.py index 0ef04c01a..c8f2399b1 100644 --- a/GrampsWebSync/grampswebsync.gpr.py +++ b/GrampsWebSync/grampswebsync.gpr.py @@ -28,7 +28,7 @@ id="gramps_web_sync", name=_("Gramps Web Sync"), description=_("Synchronizes a local database with a Gramps Web instance."), - version = '1.3.13', + version = '1.3.14', gramps_target_version="6.1", status=STABLE, fname="grampswebsync.py", From 3cea9e68f13a223dd9312fa47d1f74f779b62c7b Mon Sep 17 00:00:00 2001 From: David Straub Date: Tue, 28 Jul 2026 10:02:09 +0200 Subject: [PATCH 072/156] Update SharedPostgreSQL --- SharedPostgreSQL/shareddbapi.py | 23 +- SharedPostgreSQL/sharedpostgresql.py | 80 ++-- SharedPostgreSQL/tests/__init__.py | 0 .../tests/test_sql_translations.py | 412 ++++++++++++++++++ 4 files changed, 479 insertions(+), 36 deletions(-) create mode 100644 SharedPostgreSQL/tests/__init__.py create mode 100644 SharedPostgreSQL/tests/test_sql_translations.py diff --git a/SharedPostgreSQL/shareddbapi.py b/SharedPostgreSQL/shareddbapi.py index 2c2cf7123..8431f1d0e 100644 --- a/SharedPostgreSQL/shareddbapi.py +++ b/SharedPostgreSQL/shareddbapi.py @@ -289,7 +289,9 @@ def _create_schema(self, json_data): self.dbapi.execute( "CREATE INDEX citation_gramps_id " "ON citation(treeid,gramps_id)" ) - self.dbapi.execute("CREATE INDEX media_desc " "ON media(treeid,desc)") + self.dbapi.execute( + f"CREATE INDEX media_desc ON media(treeid,{self._quote_column('desc')})" + ) self.dbapi.execute("CREATE INDEX media_gramps_id " "ON media(treeid,gramps_id)") self.dbapi.execute("CREATE INDEX place_title " "ON place(treeid,title)") self.dbapi.execute( @@ -699,7 +701,7 @@ def get_media_handles(self, sort_handles=False, locale=glocale): self.dbapi.execute( "SELECT handle FROM media " "WHERE treeid = ? " - "ORDER BY desc " + f"ORDER BY {self._quote_column('desc')} " 'COLLATE "%s"' % locale.get_collation(), [self.dbapi.treeid], ) @@ -882,7 +884,7 @@ def _commit_raw(self, data, obj_key): else: # Insert the object: sql = ( - f"INSERT INTO %s (treeid, handle, {self.serializer.data_field}) VALUES (?, ?)" + f"INSERT INTO %s (treeid, handle, {self.serializer.data_field}) VALUES (?, ?, ?)" ) % table self.dbapi.execute( sql, [self.dbapi.treeid, handle, self.serializer.data_to_string(data)] @@ -1306,6 +1308,17 @@ def get_surname_list(self): surname_list.append(row[0]) return surname_list + def _quote_column(self, col): + """ + Return a safe column name for the current dialect. + + Override in dialect subclasses to handle reserved keywords, e.g. by + quoting or renaming them. + """ + # Mirrors the hook added by gramps PR #2178; drop once that is merged + # and shareddbapi is resynced with core dbapi. + return col + def _sql_type(self, schema_type, max_length): """ Given a schema type, return the SQL type for @@ -1346,7 +1359,7 @@ def _create_secondary_columns(self): sql_type = self._sql_type(schema_type, max_length) self.dbapi.execute( "ALTER TABLE %s ADD COLUMN %s %s" - % (table_name, field, sql_type) + % (table_name, self._quote_column(field), sql_type) ) def _update_secondary_values(self, obj): @@ -1360,7 +1373,7 @@ def _update_secondary_values(self, obj): sets = [] values = [] for field in fields: - sets.append("%s = ?" % field) + sets.append("%s = ?" % self._quote_column(field)) values.append(getattr(obj, field)) # Derived fields diff --git a/SharedPostgreSQL/sharedpostgresql.py b/SharedPostgreSQL/sharedpostgresql.py index 5fbe6ed19..98feec0b3 100644 --- a/SharedPostgreSQL/sharedpostgresql.py +++ b/SharedPostgreSQL/sharedpostgresql.py @@ -53,6 +53,21 @@ # # ------------------------------------------------------------------------- class SharedPostgreSQL(SharedDBAPI): + dialect = "postgresql" + + # Column names as they physically exist in shared PostgreSQL databases. + # "desc" is reserved in PostgreSQL and was renamed by the old blanket + # substring rewrite, which also caught "description" as a side effect. + # Both names are kept so existing databases stay readable. + _COLUMN_NAMES = {"desc": "desc_", "description": "desc_ription"} + + def _quote_column(self, col): + return self._COLUMN_NAMES.get(col, col) + + def _sql_type(self, schema_type, max_length): + result = super()._sql_type(schema_type, max_length) + return "bytea" if result == "BLOB" else result + def get_summary(self): """ Return a diction of information about this database @@ -179,18 +194,20 @@ def check_collation(self, locale): Checks that a collation exists and if not creates it. :param locale: Locale to be checked. - :param type: A GrampsLocale object. + :type locale: A GrampsLocale object. """ - # Duplicating system collations works, but to delete them the schema - # must be specified, so get the current schema collation = locale.get_collation() - self.execute( - 'CREATE COLLATION IF NOT EXISTS "%s"' - "(LOCALE = '%s')" % (collation, locale.collation) - ) + # Use pg_collation to check existence rather than IF NOT EXISTS, which + # requires PostgreSQL 12+. + self.execute("SELECT 1 FROM pg_collation WHERE collname = %s", [collation]) + if not self.fetchone(): + self.execute( + "CREATE COLLATION \"%s\" (LOCALE = '%s')" + % (collation, locale.collation) + ) def execute(self, *args, **kwargs): - sql = _hack_query(args[0]) + sql = _translate_sql(args[0]) if len(args) > 1: args = args[1] else: @@ -279,7 +296,7 @@ def execute(self, *args, **kwargs): :param kwargs: arguments to be passed to the sqlite3 execute statement :type kwargs: list """ - sql = _hack_query(args[0]) + sql = _translate_sql(args[0]) if len(args) > 1: args = args[1] else: @@ -297,25 +314,26 @@ def fetchmany(self): return None -def _hack_query(query): - query = query.replace("?", "%s") - query = query.replace("REGEXP", "~") - query = query.replace("desc", "desc_") - query = query.replace("BLOB", "bytea") - query = query.replace("INTEGER PRIMARY KEY", "SERIAL PRIMARY KEY") - ## LIMIT offset, count - ## count can be -1, for all - ## LIMIT -1 - ## LIMIT offset, -1 - query = query.replace("LIMIT -1", "LIMIT all") ## - match = re.match(".* LIMIT (.*), (.*) ", query) - if match and match.groups(): - offset, count = match.groups() - if count == "-1": - count = "all" - query = re.sub( - "(.*) LIMIT (.*), (.*) ", - "\\1 LIMIT %s OFFSET %s " % (count, offset), - query, - ) - return query +def _translate_sql(query): + """ + Translate an SQLite-flavoured SQL statement to PostgreSQL. + + :param query: the statement to translate. + :type query: str + :returns: the translated statement. + :rtype: str + """ + sql = query.replace("?", "%s") # qmark -> format paramstyle + sql = sql.replace(" REGEXP ", " ~ ") # SQLite REGEXP -> PostgreSQL ~ + sql = sql.replace("INTEGER PRIMARY KEY", "SERIAL PRIMARY KEY") + sql = re.sub(r"\bBLOB\b", "BYTEA", sql) # SQLite BLOB -> PostgreSQL BYTEA + # LIMIT offset, count -> LIMIT count OFFSET offset; a count of -1 means all + sql = re.sub( + r"\bLIMIT\s+(-?\d+)\s*,\s*(-?\d+)", + lambda m: f'LIMIT {"ALL" if m.group(2) == "-1" else m.group(2)}' + f" OFFSET {m.group(1)}", + sql, + flags=re.IGNORECASE, + ) + sql = re.sub(r"\bLIMIT\s+-1\b", "LIMIT ALL", sql, flags=re.IGNORECASE) + return sql diff --git a/SharedPostgreSQL/tests/__init__.py b/SharedPostgreSQL/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/SharedPostgreSQL/tests/test_sql_translations.py b/SharedPostgreSQL/tests/test_sql_translations.py new file mode 100644 index 000000000..08998fd1c --- /dev/null +++ b/SharedPostgreSQL/tests/test_sql_translations.py @@ -0,0 +1,412 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2015-2016 Douglas S. Blank +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Unit tests for the SharedPostgreSQL SQL dialect translations. + +These tests cover every rewrite rule applied before a query reaches psycopg2: + - qmark -> format paramstyle (? -> %s) + - REGEXP operator (REGEXP -> ~) + - autoincrement primary key (INTEGER PRIMARY KEY -> SERIAL PRIMARY KEY) + - BLOB column type (BLOB -> BYTEA) + - two-arg LIMIT (LIMIT offset, count -> LIMIT count OFFSET offset) + - unlimited LIMIT (LIMIT -1 -> LIMIT ALL) + +and the column naming applied by _quote_column(). + +psycopg2 is stubbed so no real database is required. gramps core is +required for the import chain; the whole module is skipped cleanly if +it is not present. + +Run with:: + + python3 -m unittest SharedPostgreSQL.tests.test_sql_translations -v +""" + +# ------------------------------------------------------------------------- +# +# Standard python modules +# +# ------------------------------------------------------------------------- +import os +import sys +import unittest +from unittest import mock + +# ------------------------------------------------------------------------- +# +# Stub psycopg2 before the addon is imported so no real DB driver is needed +# +# ------------------------------------------------------------------------- +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +_mock_psycopg2 = mock.MagicMock() +_mock_psycopg2.paramstyle = "format" +_mock_psycopg2.OperationalError = Exception +sys.modules.setdefault("psycopg2", _mock_psycopg2) + +# ------------------------------------------------------------------------- +# +# Gramps modules (required by the addon's import chain) +# +# ------------------------------------------------------------------------- +try: + import gramps +except ImportError as _err: + raise unittest.SkipTest("gramps package not available: %s" % _err) + +if "GRAMPS_RESOURCES" not in os.environ: + os.environ["GRAMPS_RESOURCES"] = os.path.dirname(os.path.dirname(gramps.__file__)) + +try: + from SharedPostgreSQL.sharedpostgresql import Connection, Cursor, SharedPostgreSQL +except Exception as _err: + raise unittest.SkipTest("SharedPostgreSQL module unavailable: %s" % _err) + +# The addon imports shareddbapi by bare name, the way Gramps loads addons, so +# reach the base class through the MRO rather than importing it a second time. +SharedDBAPI = SharedPostgreSQL.__bases__[0] + + +# ------------------------------------------------------------------------- +# +# Helpers +# +# ------------------------------------------------------------------------- + + +def _make_connection(): + """Return a (Connection, mock_cursor) pair without touching psycopg2.""" + conn = Connection.__new__(Connection) + cursor = mock.MagicMock() + conn._Connection__cursor = cursor + return conn, cursor + + +def _translated(sql): + """Return the SQL string that Connection.execute() would pass to psycopg2.""" + conn, cursor = _make_connection() + conn.execute(sql) + return cursor.execute.call_args[0][0] + + +# ------------------------------------------------------------------------- +# +# TestExecuteQmarkParamstyle +# +# ------------------------------------------------------------------------- +class TestExecuteQmarkParamstyle(unittest.TestCase): + """? -> %s substitution.""" + + def test_single_placeholder(self): + self.assertEqual( + _translated("SELECT * FROM person WHERE gramps_id = ?"), + "SELECT * FROM person WHERE gramps_id = %s", + ) + + def test_multiple_placeholders(self): + result = _translated("INSERT INTO t (treeid, a) VALUES (?, ?)") + self.assertEqual(result.count("%s"), 2) + self.assertNotIn("?", result) + + def test_no_placeholders_unchanged(self): + sql = "SELECT * FROM person" + self.assertEqual(_translated(sql), sql) + + +# ------------------------------------------------------------------------- +# +# TestExecuteRegexpOperator +# +# ------------------------------------------------------------------------- +class TestExecuteRegexpOperator(unittest.TestCase): + """REGEXP -> ~ substitution.""" + + def test_regexp_replaced(self): + result = _translated("SELECT * FROM person WHERE name REGEXP 'foo'") + self.assertIn(" ~ ", result) + self.assertNotIn("REGEXP", result) + + def test_no_regexp_unchanged(self): + sql = "SELECT * FROM person WHERE name = 'foo'" + self.assertEqual(_translated(sql), sql) + + +# ------------------------------------------------------------------------- +# +# TestExecuteSerialPrimaryKey +# +# ------------------------------------------------------------------------- +class TestExecuteSerialPrimaryKey(unittest.TestCase): + """INTEGER PRIMARY KEY -> SERIAL PRIMARY KEY. + + The trees table relies on the treeid being assigned automatically when a + new tree is inserted, which in PostgreSQL requires SERIAL. + """ + + def test_trees_table_uses_serial(self): + result = _translated( + "CREATE TABLE trees (treeid INTEGER PRIMARY KEY, uuid VARCHAR(32))" + ) + self.assertIn("treeid SERIAL PRIMARY KEY", result) + self.assertNotIn("INTEGER PRIMARY KEY", result) + + def test_plain_integer_column_unchanged(self): + sql = "ALTER TABLE person ADD COLUMN priority INTEGER" + self.assertEqual(_translated(sql), sql) + + +# ------------------------------------------------------------------------- +# +# TestExecuteBlobType +# +# ------------------------------------------------------------------------- +class TestExecuteBlobType(unittest.TestCase): + """BLOB -> BYTEA substitution.""" + + def test_metadata_table_blob_replaced(self): + result = _translated( + "CREATE TABLE metadata " + "(treeid INTEGER, setting VARCHAR(50), json_data TEXT, value BLOB)" + ) + self.assertIn("BYTEA", result) + self.assertNotIn("BLOB", result) + + def test_blob_data_column_replaced(self): + result = _translated( + "CREATE TABLE person " + "(treeid INTEGER, handle VARCHAR(50), blob_data BLOB)" + ) + self.assertIn("blob_data BYTEA", result) + + def test_blob_word_boundary_not_in_identifier(self): + """BLOB as part of a longer identifier is not replaced.""" + result = _translated("SELECT blobfield FROM person") + self.assertEqual(result, "SELECT blobfield FROM person") + + def test_multiple_blob_columns_all_replaced(self): + result = _translated("CREATE TABLE t (a BLOB, b TEXT, c BLOB)") + self.assertEqual(result.count("BYTEA"), 2) + self.assertNotIn("BLOB", result) + + +# ------------------------------------------------------------------------- +# +# TestExecuteLimitTranslations +# +# ------------------------------------------------------------------------- +class TestExecuteLimitTranslations(unittest.TestCase): + """LIMIT dialect translations.""" + + def test_limit_minus_one_becomes_all(self): + result = _translated("SELECT * FROM person LIMIT -1") + self.assertIn("LIMIT ALL", result) + self.assertNotIn("-1", result) + + def test_limit_offset_comma_count(self): + result = _translated("SELECT * FROM person LIMIT 5, 10") + self.assertIn("LIMIT 10 OFFSET 5", result) + + def test_limit_offset_comma_minus_one(self): + result = _translated("SELECT * FROM person LIMIT 5, -1") + self.assertIn("LIMIT ALL OFFSET 5", result) + + def test_plain_limit_unchanged(self): + result = _translated("SELECT * FROM person LIMIT 10") + self.assertEqual(result, "SELECT * FROM person LIMIT 10") + + def test_limit_with_offset_clause_unchanged(self): + result = _translated("SELECT * FROM person LIMIT 10 OFFSET 5") + self.assertEqual(result, "SELECT * FROM person LIMIT 10 OFFSET 5") + + +# ------------------------------------------------------------------------- +# +# TestExecuteLeavesIdentifiersAlone +# +# ------------------------------------------------------------------------- +class TestExecuteLeavesIdentifiersAlone(unittest.TestCase): + """Identifiers are no longer rewritten by blind substring replacement. + + The previous implementation replaced every occurrence of "desc", which + also corrupted unrelated identifiers. Column naming is now the job of + _quote_column(), so execute() must leave identifiers untouched. + """ + + def test_desc_column_not_rewritten(self): + sql = "SELECT handle FROM media ORDER BY desc_" + self.assertEqual(_translated(sql), sql) + + def test_description_not_corrupted(self): + sql = "SELECT description FROM event" + self.assertEqual(_translated(sql), sql) + + def test_descending_order_not_corrupted(self): + sql = "SELECT handle FROM person ORDER BY surname desc" + self.assertEqual(_translated(sql), sql) + + +# ------------------------------------------------------------------------- +# +# TestCursorTranslatesToo +# +# ------------------------------------------------------------------------- +class TestCursorTranslatesToo(unittest.TestCase): + """Cursor.execute applies the same translations as Connection.execute. + + Unlike core dbapi, shareddbapi passes bound parameters to cursor queries + in order to filter by treeid, so the cursor needs translation as well. + """ + + def test_cursor_translates_placeholders(self): + cursor_obj = Cursor.__new__(Cursor) + inner = mock.MagicMock() + cursor_obj._Cursor__cursor = inner + cursor_obj.execute("SELECT handle FROM person WHERE treeid = ?", [1]) + self.assertEqual( + inner.execute.call_args[0][0], + "SELECT handle FROM person WHERE treeid = %s", + ) + + +# ------------------------------------------------------------------------- +# +# TestSharedPostgreSQLSqlType +# +# ------------------------------------------------------------------------- +class TestSharedPostgreSQLSqlType(unittest.TestCase): + """SharedPostgreSQL._sql_type maps BLOB -> bytea; other types pass through.""" + + def setUp(self): + self.pg = SharedPostgreSQL.__new__(SharedPostgreSQL) + + def test_blob_becomes_bytea(self): + with mock.patch.object(SharedDBAPI, "_sql_type", return_value="BLOB"): + self.assertEqual(self.pg._sql_type("blob_field", 0), "bytea") + + def test_text_unchanged(self): + with mock.patch.object(SharedDBAPI, "_sql_type", return_value="TEXT"): + self.assertEqual(self.pg._sql_type("text_field", 255), "TEXT") + + def test_integer_unchanged(self): + with mock.patch.object(SharedDBAPI, "_sql_type", return_value="INTEGER"): + self.assertEqual(self.pg._sql_type("int_field", 0), "INTEGER") + + +# ------------------------------------------------------------------------- +# +# TestSharedPostgreSQLQuoteColumn +# +# ------------------------------------------------------------------------- +class TestSharedPostgreSQLQuoteColumn(unittest.TestCase): + """SharedPostgreSQL._quote_column returns the physical column names.""" + + def setUp(self): + self.pg = SharedPostgreSQL.__new__(SharedPostgreSQL) + + def test_desc_reserved(self): + self.assertEqual(self.pg._quote_column("desc"), "desc_") + + def test_description_keeps_legacy_name(self): + """Existing databases have desc_ription, created by the old rewrite.""" + self.assertEqual(self.pg._quote_column("description"), "desc_ription") + + def test_normal_column_unchanged(self): + self.assertEqual(self.pg._quote_column("gramps_id"), "gramps_id") + + def test_handle_unchanged(self): + self.assertEqual(self.pg._quote_column("handle"), "handle") + + def test_change_unchanged(self): + self.assertEqual(self.pg._quote_column("change"), "change") + + def test_base_class_is_identity(self): + base = SharedDBAPI.__new__(SharedDBAPI) + self.assertEqual(base._quote_column("desc"), "desc") + + +# ------------------------------------------------------------------------- +# +# TestSecondaryColumnNaming +# +# ------------------------------------------------------------------------- +class TestSecondaryColumnNaming(unittest.TestCase): + """Every Gramps secondary field maps to the column an existing database has. + + Guards against a rename of the two fields whose physical column names were + fixed by the previous substring rewrite. + """ + + def setUp(self): + self.pg = SharedPostgreSQL.__new__(SharedPostgreSQL) + + def test_media_desc_field(self): + from gramps.gen.lib import Media + + fields = [field[0] for field in Media.get_secondary_fields()] + self.assertIn("desc", fields) + self.assertEqual(self.pg._quote_column("desc"), "desc_") + + def test_event_description_field(self): + from gramps.gen.lib import Event + + fields = [field[0] for field in Event.get_secondary_fields()] + self.assertIn("description", fields) + self.assertEqual(self.pg._quote_column("description"), "desc_ription") + + def test_no_other_field_needs_renaming(self): + """Only desc and description were affected by the old rewrite.""" + from gramps.gen.lib import ( + Citation, + Event, + Family, + Media, + Note, + Person, + Place, + Repository, + Source, + Tag, + ) + + affected = set() + for cls in ( + Person, + Family, + Event, + Place, + Repository, + Source, + Citation, + Media, + Note, + Tag, + ): + for field, _type, _length in cls.get_secondary_fields(): + if "desc" in field: + affected.add(field) + self.assertEqual(affected, {"desc", "description"}) + + +if __name__ == "__main__": + unittest.main() From d4ea33b68824df35898d9575b41fe89a31887fc5 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Fri, 31 Jul 2026 11:53:38 -0700 Subject: [PATCH 073/156] Merge Update SharedPostgreSQL to match PostgreSQL addon- #1001 --- SharedPostgreSQL/sharedpostgresql.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedPostgreSQL/sharedpostgresql.gpr.py b/SharedPostgreSQL/sharedpostgresql.gpr.py index fa6703815..dcff025c7 100644 --- a/SharedPostgreSQL/sharedpostgresql.gpr.py +++ b/SharedPostgreSQL/sharedpostgresql.gpr.py @@ -24,7 +24,7 @@ name=_("SharedPostgreSQL"), name_accell=_("Shared _PostgreSQL Database"), description=_("Shared PostgreSQL Database"), - version = '0.1.16', + version = '0.1.17', gramps_target_version="6.1", status=STABLE, fname="sharedpostgresql.py", From ec6486d2778dcd87fe2f9461f157181dd714ee21 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 31 Jul 2026 15:52:48 +0200 Subject: [PATCH 074/156] Gramps Web Sync refactoring --- GrampsWebSync/adapters.py | 488 ++++++++++++++++++++--- GrampsWebSync/const.py | 6 +- GrampsWebSync/diffhandler.py | 11 - GrampsWebSync/grampswebsync.py | 232 +++++++++-- GrampsWebSync/session.py | 474 +++++++++++++++++----- GrampsWebSync/tests/fakes.py | 45 ++- GrampsWebSync/tests/scenario.py | 9 +- GrampsWebSync/tests/test_adapters.py | 52 ++- GrampsWebSync/tests/test_credentials.py | 386 ++++++++++++++++++ GrampsWebSync/tests/test_errors.py | 35 +- GrampsWebSync/tests/test_recovery.py | 237 +++++++++++ GrampsWebSync/tests/test_sync_flow.py | 38 +- GrampsWebSync/tests/test_view_mapping.py | 25 +- GrampsWebSync/webapihandler.py | 92 +++-- 14 files changed, 1855 insertions(+), 275 deletions(-) create mode 100644 GrampsWebSync/tests/test_credentials.py create mode 100644 GrampsWebSync/tests/test_recovery.py diff --git a/GrampsWebSync/adapters.py b/GrampsWebSync/adapters.py index d941ac571..97cc971ef 100644 --- a/GrampsWebSync/adapters.py +++ b/GrampsWebSync/adapters.py @@ -16,14 +16,26 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -"""Production implementations of the :mod:`session` ports.""" +"""Production implementations of the :mod:`session` ports. + +Two task runners are provided rather than one. :class:`GLibTaskRunner` keeps a +step on the GTK main loop, which is mandatory for anything touching a Gramps +database: the sqlite backend binds a connection to its creating thread. +:class:`IoRunner` moves a step to a worker thread, which is where the network +calls belong -- they are the only part of a sync that can block indefinitely. + +:class:`ConfigCredentialStore` keeps one entry per ``(url, username)`` pair, so +each server carries its own sync baseline. +""" from __future__ import annotations import logging import os +import threading import time from collections.abc import Callable +from dataclasses import dataclass from typing import Any from gi.repository import GLib @@ -32,74 +44,378 @@ LOG = logging.getLogger("grampswebsync") +#: Keys the pre-multi-server versions of the addon used. Still written, as a +#: mirror of the last-used entry, so that downgrading keeps working. +LEGACY_URL = "credentials.url" +LEGACY_USERNAME = "credentials.username" +LEGACY_TIMESTAMP = "credentials.timestamp" + +#: snapd interface granting access to ``org.freedesktop.secrets``. Declared by +#: the Gramps snap but manually connected, so it is off until the user says so. +SNAP_KEYRING_INTERFACE = "password-manager-service" + + +def normalize_url(url: str) -> str: + """Return ``url`` in the form used as a credential-store key. + + Normalizing here rather than in :class:`webapihandler.WebApiHandler` keeps a + stray trailing slash from looking like a different server, which would + otherwise cost the entry its sync baseline. + + :param url: The URL as typed or stored. + :returns: The URL without surrounding whitespace or trailing slashes. + """ + return url.strip().rstrip("/") + + +# ------------------------------------------------------------ +# +# Keyring +# +# ------------------------------------------------------------ +@dataclass(frozen=True) +class KeyringUnavailable: + """A keyring call that failed, for the view to report. + + :param detail: The underlying exception text, for logs and details views. + :param snap_command: The ``snap connect`` command that would fix it, when + running confined under snap; ``None`` elsewhere. + """ + + detail: str + snap_command: str | None = None + -def get_password(service: str, username: str) -> str | None: - """Return the stored password for ``username``, if a keyring is available. +def snap_connect_command() -> str | None: + """Return the command connecting the keyring interface, under snap only. - :param service: Keyring service name; the server URL is used. - :param username: The account whose password is wanted. - :returns: The password, or ``None`` if unavailable. + ``SNAP_INSTANCE_NAME`` rather than ``SNAP_NAME`` is what makes the command + correct under a parallel install. + + :returns: The command, or ``None`` when not running as a snap. """ - LOG.debug("Retrieving password for user %s", username) - try: - import keyring - except ImportError: - LOG.warning("Keyring is not installed, cannot retrieve password.") + if not os.environ.get("SNAP"): return None - return keyring.get_password(service, username) + name = ( + os.environ.get("SNAP_INSTANCE_NAME") + or os.environ.get("SNAP_NAME") + or "gramps" + ) + return f"snap connect {name}:{SNAP_KEYRING_INTERFACE}" -def set_password(service: str, username: str, password: str) -> None: - """Store ``password`` in the keyring, if one is available.""" - try: - import keyring - except ImportError: - return - LOG.debug("Storing password for user %s", username) - keyring.set_password(service, username, password) +class Keyring: + """The system keyring, degrading to unavailable instead of raising. + Every call is guarded with a bare ``except Exception``. The failures seen in + practice do not derive from ``keyring.errors``: under snap confinement the + Secret Service backend raises ``jeepney.wrappers.DBusErrorResponse``, from a + transitive dependency, so catching the keyring package's own hierarchy is + not enough. -class ConfigCredentialStore: - """Credentials in the Gramps config file, password in the system keyring.""" + After a failure the keyring is marked unavailable and no further calls are + attempted for the lifetime of this object. + """ def __init__(self) -> None: - self.config = configman.register_manager("webapisync") - self.config.register("credentials.url", "") - self.config.register("credentials.username", "") - self.config.register("credentials.timestamp", 0) - self.config.load() + self.unavailable: KeyringUnavailable | None = None - def get_url(self) -> str: - return self.config.get("credentials.url") + def _module(self): + """Return the ``keyring`` module, or ``None`` if it cannot be used.""" + if self.unavailable is not None: + return None + try: + import keyring + except Exception as exc: # noqa: BLE001 -- absence is not an error here + LOG.warning("Keyring is not available: %s", exc) + self.unavailable = KeyringUnavailable(str(exc), snap_connect_command()) + return None + return keyring - def get_username(self) -> str: - return self.config.get("credentials.username") + def _failed(self, action: str, exc: Exception) -> None: + """Record that ``action`` failed and stop using the keyring.""" + LOG.warning("Keyring %s failed: %s", action, exc) + self.unavailable = KeyringUnavailable(str(exc), snap_connect_command()) - def get_password(self) -> str | None: - url = self.get_url() - username = self.get_username() - if not url or not username: + def get(self, service: str, username: str) -> str | None: + """Return the stored password, or ``None`` if it cannot be read.""" + keyring = self._module() + if keyring is None: + return None + try: + return keyring.get_password(service, username) + except Exception as exc: # noqa: BLE001 -- reported through `unavailable` + self._failed("read", exc) return None - return get_password(url, username) - def get_timestamp(self) -> float: - return self.config.get("credentials.timestamp") + def set(self, service: str, username: str, password: str) -> bool: + """Store ``password``. Returns whether it was actually stored.""" + keyring = self._module() + if keyring is None: + return False + try: + keyring.set_password(service, username, password) + except Exception as exc: # noqa: BLE001 -- reported through `unavailable` + self._failed("write", exc) + return False + return True + + def delete(self, service: str, username: str) -> None: + """Remove a stored password, ignoring one that was never there.""" + keyring = self._module() + if keyring is None: + return + try: + keyring.delete_password(service, username) + except Exception as exc: # noqa: BLE001 -- absent entries raise too + LOG.debug("Keyring delete for %s failed: %s", username, exc) + + +# ------------------------------------------------------------ +# +# Credential store +# +# ------------------------------------------------------------ +class ConfigCredentialStore: + """Server entries in the Gramps config file, passwords in the keyring. - def set_timestamp(self, timestamp: float) -> None: - LOG.debug("Recording last successful sync at %s", timestamp) - self.config.set("credentials.timestamp", timestamp) + Each entry is keyed by ``(url, username)`` -- which identifies a tree, since + a Gramps Web account belongs to exactly one -- and carries its own + ``timestamp``, the baseline the diff uses. Per-entry baselines are why + switching servers no longer discards one. + """ + + def __init__(self, keyring: Keyring | None = None, config: Any = None) -> None: + """Initialize the store. + + :param keyring: Password storage. A real one is built if omitted. + :param config: An already-registered config manager. Tests pass one + pointed at a temporary directory so a run cannot write to the + user's own Gramps configuration. + """ + self.keyring = keyring if keyring is not None else Keyring() + self.config = ( + config if config is not None else configman.register_manager("webapisync") + ) + self.config.register(LEGACY_URL, "") + self.config.register(LEGACY_USERNAME, "") + self.config.register(LEGACY_TIMESTAMP, 0) + self.config.register("credentials.servers", []) + self.config.register("credentials.last_used", []) + self.config.load() + self._reconcile_legacy() + + # -------------------------------------------------------- + # Raw access + # -------------------------------------------------------- + def _servers(self) -> list[dict[str, Any]]: + """Return the stored entries, tolerating a corrupted config value. + + A value the config manager could not parse is stored as ``None`` rather + than falling back to the registered default, so the type has to be + checked rather than assumed. + """ + servers = self.config.get("credentials.servers") + if not isinstance(servers, list): + LOG.warning("Ignoring unreadable server list in config.") + return [] + return [entry for entry in servers if isinstance(entry, dict)] + + def _find( + self, servers: list[dict[str, Any]], url: str, username: str + ) -> dict[str, Any] | None: + """Return the entry for ``(url, username)``, or ``None``.""" + url = normalize_url(url) + for entry in servers: + if normalize_url(entry.get("url", "")) == url and ( + entry.get("username", "") == username + ): + return entry + return None + + def _last_used(self) -> tuple[str, str] | None: + """Return the ``(url, username)`` last synced, if any.""" + pair = self.config.get("credentials.last_used") + if isinstance(pair, list) and len(pair) == 2: + return str(pair[0]), str(pair[1]) + return None + + def _current(self) -> dict[str, Any] | None: + """Return the last-used entry, falling back to the only one stored.""" + servers = self._servers() + pair = self._last_used() + if pair is not None: + entry = self._find(servers, *pair) + if entry is not None: + return entry + return servers[0] if len(servers) == 1 else None + + def _write(self, servers: list[dict[str, Any]]) -> None: + """Persist the entry list and the legacy mirror, then save.""" + self.config.set("credentials.servers", servers) + self._write_legacy_mirror(servers) self.config.save() - def save_credentials(self, url: str, username: str, password: str) -> None: - """Persist the credentials, resetting the sync time if the URL changed.""" - if url != self.get_url(): - self.config.set("credentials.timestamp", 0) - self.config.set("credentials.url", url) - self.config.set("credentials.username", username) - set_password(url, username, password) + def _write_legacy_mirror(self, servers: list[dict[str, Any]]) -> None: + """Mirror the last-used entry into the pre-multi-server keys. + + An older version of the addon reads only those keys. Keeping them + current means a downgrade finds its credentials and its baseline where + it expects them, instead of resyncing from scratch. + """ + pair = self._last_used() + entry = self._find(servers, *pair) if pair is not None else None + if entry is None: + self.config.set(LEGACY_URL, "") + self.config.set(LEGACY_USERNAME, "") + self.config.set(LEGACY_TIMESTAMP, 0) + return + self.config.set(LEGACY_URL, entry.get("url", "")) + self.config.set(LEGACY_USERNAME, entry.get("username", "")) + self.config.set(LEGACY_TIMESTAMP, int(entry.get("timestamp", 0) or 0)) + + def _reconcile_legacy(self) -> None: + """Fold the legacy keys into the entry list. + + Covers both cases in one path: on first run after an upgrade there is no + matching entry and the legacy triple becomes one, and after a downgrade + and back the entry exists but an older version may have synced in the + meantime, so the later of the two baselines wins. + """ + legacy_url = normalize_url(self.config.get(LEGACY_URL) or "") + if not legacy_url: + return + legacy_username = self.config.get(LEGACY_USERNAME) or "" + legacy_timestamp = float(self.config.get(LEGACY_TIMESTAMP) or 0) + + servers = self._servers() + entry = self._find(servers, legacy_url, legacy_username) + if entry is None: + LOG.info("Migrating stored credentials to the server list.") + servers.append( + { + "url": legacy_url, + "username": legacy_username, + "timestamp": legacy_timestamp, + "remember_password": True, + } + ) + self.config.set("credentials.last_used", [legacy_url, legacy_username]) + elif legacy_timestamp > float(entry.get("timestamp", 0) or 0): + entry["timestamp"] = legacy_timestamp + else: + return + self.config.set("credentials.servers", servers) self.config.save() + # -------------------------------------------------------- + # CredentialStore protocol + # -------------------------------------------------------- + def get_url(self) -> str: + """Return the last-used server URL, for pre-filling the login page.""" + entry = self._current() + return entry.get("url", "") if entry else "" + def get_username(self) -> str: + """Return the last-used user name.""" + entry = self._current() + return entry.get("username", "") if entry else "" + + def get_password(self) -> str | None: + """Return the last-used password, if one was stored and is readable.""" + entry = self._current() + if not entry or not entry.get("remember_password", True): + return None + url = entry.get("url", "") + username = entry.get("username", "") + if not url or not username: + return None + return self.keyring.get(url, username) + + def get_timestamp(self, url: str, username: str) -> float: + """Return the sync baseline for one server. + + :param url: The server URL. + :param username: The account on that server. + :returns: The last successful sync time, or ``0`` if never synced. + """ + entry = self._find(self._servers(), url, username) + return float(entry.get("timestamp", 0) or 0) if entry else 0.0 + + def set_timestamp(self, url: str, username: str, timestamp: float) -> None: + """Record a successful sync against one server.""" + servers = self._servers() + entry = self._find(servers, url, username) + if entry is None: + entry = { + "url": normalize_url(url), + "username": username, + "remember_password": True, + } + servers.append(entry) + entry["timestamp"] = timestamp + LOG.debug("Recording last successful sync at %s", timestamp) + self.config.set("credentials.last_used", [normalize_url(url), username]) + self._write(servers) + + def save_credentials( + self, url: str, username: str, password: str, remember_password: bool = True + ) -> None: + """Persist one server entry, and its password if asked to. + + The entry itself is always stored: it carries the sync baseline, which + is not a credential, and discarding it would make every later run a cold + sync. ``remember_password`` governs the keyring only. + + :param url: The server URL, already sanitized by the caller. + :param username: The account name. + :param password: The password, stored only if ``remember_password``. + :param remember_password: Whether the password may go to the keyring. + """ + url = normalize_url(url) + servers = self._servers() + entry = self._find(servers, url, username) + if entry is None: + entry = {"url": url, "username": username, "timestamp": 0.0} + servers.append(entry) + entry["remember_password"] = remember_password + + if remember_password: + self.keyring.set(url, username, password) + else: + # Turning the setting off has to erase what is already stored, not + # merely stop writing, or it appears to do nothing. + self.keyring.delete(url, username) + + self.config.set("credentials.last_used", [url, username]) + self._write(servers) + + def forget(self, url: str, username: str) -> None: + """Remove one server entry entirely, keyring item included.""" + url = normalize_url(url) + servers = [ + entry + for entry in self._servers() + if not ( + normalize_url(entry.get("url", "")) == url + and entry.get("username", "") == username + ) + ] + self.keyring.delete(url, username) + if self._last_used() == (url, username): + self.config.set("credentials.last_used", []) + self._write(servers) + + def keyring_error(self) -> KeyringUnavailable | None: + """Return the keyring failure to report, if one has occurred.""" + return self.keyring.unavailable + + +# ------------------------------------------------------------ +# +# Media +# +# ------------------------------------------------------------ class GrampsMediaStore: """Resolves media paths against the open Gramps database's media path. @@ -118,14 +434,32 @@ def exists(self, media: Any) -> bool: return os.path.exists(self.full_path(media)) +# ------------------------------------------------------------ +# +# Task runners +# +# ------------------------------------------------------------ +def _post_to_main_loop(func: Callable[[], None]) -> None: + """Schedule ``func`` to run once on the GTK main loop.""" + + def once() -> bool: + func() + return False + + GLib.idle_add(once) + + class GLibTaskRunner: """Defers a task to the GTK main loop. - The task must not run on a worker thread: it drives Gramps progress - through the GUI :class:`gramps.gui.user.User`, which touches widgets, and - GTK is not thread-safe -- doing so segfaults inside ``diff_dbs``. - :func:`GLib.idle_add` keeps the work on the main loop while still letting - the caller return so the assistant can paint the progress page first. + For steps that touch a Gramps database. Those must not run on a worker + thread: the sqlite backend passes no ``check_same_thread=False`` and shares + one cursor, so a connection is usable only from the thread that created it. + They also drive Gramps progress through the GUI + :class:`gramps.gui.user.User`, which touches widgets. + + :func:`GLib.idle_add` keeps the work on the main loop while still letting the + caller return, so the view can paint the progress page first. """ def run( @@ -147,6 +481,54 @@ def once() -> bool: GLib.idle_add(once) + def post(self, func: Callable[[], None]) -> None: + """Run ``func`` on the main loop.""" + _post_to_main_loop(func) + + +class IoRunner: + """Runs a task on a worker thread, dispatching the outcome on the main loop. + + For steps that only do network I/O. Those are where a sync spends most of + its wall-clock time and the only place it can block indefinitely, so moving + them off the main loop is what makes the window stay responsive and Cancel + actually work. Callbacks are marshalled back through + :func:`GLib.idle_add`, so listeners still run on the thread that owns GTK. + """ + + def run( + self, + func: Callable[[], Any], + on_success: Callable[[Any], None], + on_error: Callable[[BaseException], None], + ) -> None: + """Run ``func`` on a worker thread; call back on the main loop.""" + + def work() -> None: + try: + result = func() + except BaseException as exc: # noqa: BLE001 -- reported, not swallowed + # Handed straight on: `except ... as exc` unbinds the name when + # the block exits, and the callback runs later than that. + self._dispatch(on_error, exc) + else: + self._dispatch(on_success, result) + + threading.Thread(target=work, daemon=True, name="grampswebsync-io").start() + + @staticmethod + def _dispatch(callback: Callable[[Any], None], value: Any) -> None: + """Deliver ``value`` to ``callback`` on the main loop.""" + _post_to_main_loop(lambda: callback(value)) + + def post(self, func: Callable[[], None]) -> None: + """Run ``func`` on the main loop. + + Progress raised inside a network step arrives here, so that listeners + drawing widgets never run on the worker thread. + """ + _post_to_main_loop(func) + class SystemClock: """The wall clock.""" diff --git a/GrampsWebSync/const.py b/GrampsWebSync/const.py index 83eb4c779..a17dcd90a 100644 --- a/GrampsWebSync/const.py +++ b/GrampsWebSync/const.py @@ -65,4 +65,8 @@ MODE_BIDIRECTIONAL = 0 MODE_RESET_TO_LOCAL = 1 MODE_RESET_TO_REMOTE = 2 -MODE_MERGE = 3 \ No newline at end of file + +#: Modes that delete rather than propagate. The view warns about these instead +#: of presenting them as equal-weight peers of the default. Display text lives +#: in the view, which is where translation happens. +DESTRUCTIVE_MODES = frozenset({MODE_RESET_TO_LOCAL, MODE_RESET_TO_REMOTE}) \ No newline at end of file diff --git a/GrampsWebSync/diffhandler.py b/GrampsWebSync/diffhandler.py index ca7e56adb..7e43c40da 100644 --- a/GrampsWebSync/diffhandler.py +++ b/GrampsWebSync/diffhandler.py @@ -48,7 +48,6 @@ MODE_BIDIRECTIONAL, MODE_RESET_TO_LOCAL, MODE_RESET_TO_REMOTE, - MODE_MERGE, OBJ_LST, Action, Actions, @@ -344,16 +343,6 @@ def changes_to_actions(changes, sync_mode: int) -> Actions: C_UPD_LOC: A_UPD_LOC, C_UPD_REM: A_UPD_LOC, } - elif sync_mode == MODE_MERGE: - change_to_action = { - C_UPD_BOTH: A_MRG_REM, - C_ADD_LOC: A_ADD_REM, - C_ADD_REM: A_ADD_LOC, - C_DEL_LOC: A_ADD_LOC, - C_DEL_REM: A_ADD_REM, - C_UPD_LOC: A_UPD_REM, - C_UPD_REM: A_UPD_LOC, - } else: raise ValueError(f"Invalid sync mode: {sync_mode}") actions = [] diff --git a/GrampsWebSync/grampswebsync.py b/GrampsWebSync/grampswebsync.py index 9b91d4082..4bb25b4b2 100644 --- a/GrampsWebSync/grampswebsync.py +++ b/GrampsWebSync/grampswebsync.py @@ -36,6 +36,8 @@ ConfigCredentialStore, GLibTaskRunner, GrampsMediaStore, + IoRunner, + KeyringUnavailable, SystemClock, ) from const import ( @@ -46,14 +48,14 @@ C_UPD_BOTH, C_UPD_LOC, C_UPD_REM, + DESTRUCTIVE_MODES, MODE_BIDIRECTIONAL, - MODE_MERGE, MODE_RESET_TO_LOCAL, MODE_RESET_TO_REMOTE, Actions, ) from diffhandler import changes_to_actions, has_local_actions, has_remote_actions -from gi.repository import Gtk +from gi.repository import GLib, Gtk from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.lib import Tag from gramps.gui.dialog import QuestionDialog2 @@ -108,6 +110,26 @@ } +def keyring_message(problem: KeyringUnavailable) -> str: + """Return the localized notice for an unusable keyring. + + Under snap the failure is a confinement setting the user can change, so the + message carries the command rather than only apologizing. + + :param problem: What the keyring reported. + :returns: A message suitable for display. + """ + if problem.snap_command: + return _( + "The password could not be saved to the system keyring. " + "Snap confinement blocks access until you run: %s" + ) % problem.snap_command + return _( + "The password could not be saved to the system keyring. " + "You will need to enter it each time." + ) + + def error_message(kind: ErrorKind, detail: str = "") -> str: """Return the localized message for an error kind. @@ -144,7 +166,13 @@ def error_message(kind: ErrorKind, detail: str = "") -> str: "Unable to synchronize changes to server: objects have been modified." ), ErrorKind.APPLY_FAILED: _("Unexpected error while applying changes."), + ErrorKind.STALE_LOCAL_DATA: _( + "The family tree was modified while the changes were being " + "reviewed. Nothing has been applied. Please compare again." + ), } + if kind is ErrorKind.SERVER_TASK_FAILED: + return _("The server could not apply the changes: %s") % detail if kind is ErrorKind.SERVER_ERROR: return _("Server error %s. Please check your connection.") % detail if kind is ErrorKind.UNEXPECTED: @@ -159,6 +187,11 @@ def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: """Build the assistant and the session behind it.""" LOG.debug("Initializing Gramps Web Sync addon.") BatchTool.__init__(self, dbstate, user, options_class, name) + if self.fail: + # The user declined the undo-history warning; honour that instead + # of opening the assistant anyway. + LOG.debug("Undo history warning declined; not opening the tool.") + return ManagedWindow.__init__(self, user.uistate, [], self.__class__) self.dbstate = dbstate @@ -171,6 +204,7 @@ def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: credentials=self.credentials, media=GrampsMediaStore(dbstate.db), runner=GLibTaskRunner(), + io_runner=IoRunner(), clock=SystemClock(), listener=self, ) @@ -223,7 +257,7 @@ def __init__(self, dbstate, user, options_class, name, *args, **kwargs) -> None: _("Progress Information"), ) - self.conclusion = ConclusionPage(self.assistant) + self.conclusion = ConclusionPage(self.assistant, on_retry=self.on_retry) self.add_page(self.conclusion, Gtk.AssistantPageType.SUMMARY, _("Summary")) self.show() @@ -265,6 +299,10 @@ def _make_backend(self, url: str, username: str, password: str) -> WebApiHandler """Build the real Web API handler. Injected into the session.""" return WebApiHandler(url, username, password, None) + def on_retry(self, _button) -> None: + """Resume a failed run from the step that failed.""" + self.session.retry() + # -------------------------------------------------------- # SessionListener # -------------------------------------------------------- @@ -335,6 +373,7 @@ def prepare(self, assistant, page): self.loginpage.show_error(error_message(error.kind, error.detail)) else: self.loginpage.clear_error() + self._show_keyring_notice(self.loginpage) elif page is self.diff_progress_page: if self.session.state is State.LOGIN: @@ -370,6 +409,17 @@ def prepare(self, assistant, page): elif page is self.conclusion: self.conclusion.prepare(self.session) + self._show_keyring_notice(self.conclusion) + + def _show_keyring_notice(self, page) -> None: + """Tell the user if the keyring could not be used. + + Reported rather than swallowed: without this the password field is + simply empty every run and nothing explains why. + """ + problem = self.credentials.keyring_error() + if problem is not None: + page.show_notice(keyring_message(problem)) def sanitize_url(self, url: str) -> str: """Prepend https if no scheme is given, and warn about plain http. @@ -505,14 +555,30 @@ def __init__(self, assistant, url, username, password): self.error_label.hide() grid.attach(self.error_label, 0, 3, 2, 1) + # Non-fatal notice, e.g. an unusable keyring. Distinct from the error + # label because it does not stop the user from continuing. + self.notice_label = Gtk.Label() + self.notice_label.set_line_wrap(True) + self.notice_label.set_max_width_chars(60) + self.notice_label.set_no_show_all(True) + self.notice_label.hide() + grid.attach(self.notice_label, 0, 4, 2, 1) + # Connect entry change events self.url.connect("changed", self.on_entry_changed) self.username.connect("changed", self.on_entry_changed) self.password.connect("changed", self.on_entry_changed) def show_error(self, message: str): - """Display an error message on the login page.""" - self.error_label.set_markup(f"Error: {message}") + """Display an error message on the login page. + + The message is escaped: it can carry server or exception text, and an + unescaped ``&`` or ``<`` would break the markup or swallow the message. + """ + label = GLib.markup_escape_text(_("Error:")) + self.error_label.set_markup( + f"{label} {GLib.markup_escape_text(message)}" + ) self.error_label.show() self.update_complete() @@ -521,6 +587,11 @@ def clear_error(self): self.error_label.hide() self.update_complete() + def show_notice(self, message: str): + """Display a non-fatal notice, such as an unusable keyring.""" + self.notice_label.set_markup(f"{GLib.markup_escape_text(message)}") + self.notice_label.show() + @property def complete(self): url = self.url.get_text() @@ -572,47 +643,54 @@ def __init__(self, assistant): scrolled_window.add(self.tree_view) self.sync_label = Gtk.Label() - self.sync_label.set_text("Sync mode") + self.sync_label.set_text(_("Sync mode")) # Box for radio buttons self.radio_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) - # Radio buttons - option_name = _("Bidirectional Synchronization") - self.radio_button1 = Gtk.RadioButton.new_with_label_from_widget( - None, option_name - ) - self.radio_button1.connect( - "toggled", self.on_radio_button_toggled, MODE_BIDIRECTIONAL - ) - self.radio_box.pack_start(self.radio_button1, False, False, 0) - - option_name = _("Reset remote to local") - self.radio_button2 = Gtk.RadioButton.new_from_widget(self.radio_button1) - self.radio_button2.set_label(option_name) - self.radio_button2.connect( - "toggled", self.on_radio_button_toggled, MODE_RESET_TO_LOCAL - ) - self.radio_box.pack_start(self.radio_button2, False, False, 0) + #: Mode -> the one-line explanation shown when it is selected. With + #: per-object selection out of scope this is the user's only control, + #: so each option has to say what it will do. + self.mode_descriptions = { + MODE_BIDIRECTIONAL: _( + "Changes from both sides are combined. Objects edited in both " + "places are merged." + ), + MODE_RESET_TO_LOCAL: _( + "The server is made to match this computer. Anything changed " + "only on the server is discarded." + ), + MODE_RESET_TO_REMOTE: _( + "This computer is made to match the server. Anything changed " + "only here is discarded." + ), + } - option_name = _("Reset local to remote") - self.radio_button3 = Gtk.RadioButton.new_from_widget(self.radio_button1) - self.radio_button3.set_label(option_name) - self.radio_button3.connect( - "toggled", self.on_radio_button_toggled, MODE_RESET_TO_REMOTE - ) - self.radio_box.pack_start(self.radio_button3, False, False, 0) + first = None + for mode, label in ( + (MODE_BIDIRECTIONAL, _("Bidirectional Synchronization")), + (MODE_RESET_TO_LOCAL, _("Reset remote to local")), + (MODE_RESET_TO_REMOTE, _("Reset local to remote")), + ): + if first is None: + button = Gtk.RadioButton.new_with_label_from_widget(None, label) + first = button + else: + button = Gtk.RadioButton.new_from_widget(first) + button.set_label(label) + button.connect("toggled", self.on_radio_button_toggled, mode) + self.radio_box.pack_start(button, False, False, 0) - option_name = _("Merge") - self.radio_button4 = Gtk.RadioButton.new_from_widget(self.radio_button1) - self.radio_button4.set_label(option_name) - self.radio_button4.connect("toggled", self.on_radio_button_toggled, MODE_MERGE) - self.radio_box.pack_start(self.radio_button4, False, False, 0) + self.description_label = Gtk.Label() + self.description_label.set_line_wrap(True) + self.description_label.set_max_width_chars(70) + self.description_label.set_xalign(0) # Box to hold the label and radio buttons self.label_radio_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) self.label_radio_box.pack_start(self.sync_label, False, False, 0) self.label_radio_box.pack_start(self.radio_box, False, False, 0) + self.label_radio_box.pack_start(self.description_label, False, False, 0) self.outer_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) self.outer_box.pack_start(scrolled_window, True, True, 0) @@ -624,6 +702,18 @@ def on_radio_button_toggled(self, button, name): """Callback for radio buttons setting sync mode.""" if button.get_active(): self.sync_mode = int(name) + self.update_description() + + def update_description(self): + """Describe the selected mode, warning if it deletes.""" + text = self.mode_descriptions.get(self.sync_mode, "") + if self.sync_mode in DESTRUCTIVE_MODES: + self.description_label.set_markup( + f"{GLib.markup_escape_text(_('Warning:'))} " + f"{GLib.markup_escape_text(text)}" + ) + else: + self.description_label.set_text(text) def prepare(self, changes: Actions): """Convert the changes list to a tree store.""" @@ -674,6 +764,7 @@ def prepare(self, changes: Actions): for i, row in enumerate(self.store): self.tree_view.expand_row(Gtk.TreePath(i), False) + self.update_description() self.set_complete() @@ -846,9 +937,13 @@ def update_progress(self, kind: str, fraction: float): class ConclusionPage(Page): - """The conclusion page, reporting either the outcome or the error.""" + """The conclusion page, reporting either the outcome or the error. - def __init__(self, assistant): + :param assistant: The assistant owning this page. + :param on_retry: Called when the user asks to resume a failed run. + """ + + def __init__(self, assistant, on_retry=None): super().__init__(assistant) label = Gtk.Label(label="") label.set_line_wrap(True) @@ -857,19 +952,74 @@ def __init__(self, assistant): self.label = label self.pack_start(self.label, False, False, 0) + self.notice_label = Gtk.Label() + self.notice_label.set_line_wrap(True) + self.notice_label.set_max_width_chars(60) + self.notice_label.set_no_show_all(True) + self.notice_label.hide() + self.pack_start(self.notice_label, False, False, 10) + + # A failed run would otherwise be a dead end: the only button on a + # summary page is Close, and reopening the tool re-downloads and + # re-diffs the whole tree for what is usually a transient problem. + self.retry_button = Gtk.Button(label=_("Try again")) + self.retry_button.set_halign(Gtk.Align.CENTER) + self.retry_button.set_no_show_all(True) + self.retry_button.hide() + if on_retry is not None: + self.retry_button.connect("clicked", on_retry) + self.pack_start(self.retry_button, False, False, 10) + def prepare(self, session: SyncSession) -> None: """Render the final message for ``session``.""" if session.error is not None: self.label.set_text( error_message(session.error.kind, session.error.detail) ) - elif not session.downloaded and not session.uploaded: - self.label.set_text(_("Media files are in sync.")) - LOG.info("Media files are in sync.") else: - self.label.set_text(self._transfer_summary(session)) + self.label.set_text(self._outcome_summary(session)) + self.retry_button.set_visible(session.can_retry) self.set_complete() + def show_notice(self, message: str) -> None: + """Show a non-fatal notice alongside the outcome.""" + self.notice_label.set_markup(f"{GLib.markup_escape_text(message)}") + self.notice_label.show() + + def _outcome_summary(self, session: SyncSession) -> str: + """Describe what the run actually did, to both trees and to media. + + The old summary reported media only, so a run that applied hundreds of + object changes and moved no files said "Media files are in sync." + """ + parts = [] + applied = len(session.actions) + if applied: + parts.append( + ngettext("Applied %s change.", "Applied %s changes.", applied) + % applied + ) + transfer = self._transfer_summary(session) + if transfer: + parts.append(transfer) + elif not session.missing_both: + parts.append(_("Media files are in sync.")) + if session.missing_both: + count = len(session.missing_both) + parts.append( + ngettext( + "%s media file is missing on both sides and could not be " + "transferred.", + "%s media files are missing on both sides and could not be " + "transferred.", + count, + ) + % count + ) + if not parts: + parts.append(_("Both trees are already in sync.")) + return " ".join(parts) + @staticmethod def _transfer_summary(session: SyncSession) -> str: """Summarize how many media files moved, and how many failed.""" diff --git a/GrampsWebSync/session.py b/GrampsWebSync/session.py index 83dd8c87a..bd5357c93 100644 --- a/GrampsWebSync/session.py +++ b/GrampsWebSync/session.py @@ -22,12 +22,18 @@ progressing through the stages in :class:`State`. Callers drive it with :meth:`~SyncSession.begin`, :meth:`~SyncSession.submit_credentials`, :meth:`~SyncSession.confirm_changes` and :meth:`~SyncSession.confirm_files`, -and observe it through a :class:`SessionListener`. +and observe it through a :class:`SessionListener`. A failed run can be resumed +with :meth:`~SyncSession.retry`. Collaborators are supplied as ports: :class:`Backend`, :class:`CredentialStore`, :class:`MediaStore`, :class:`TaskRunner` and :class:`Clock`. Failures are recorded as a :class:`SyncError` carrying an :class:`ErrorKind`; callers are responsible for localizing them. + +Each stage is split into the part that touches a database and the part that +talks to the network, because only the latter may leave the main loop -- see +:class:`adapters.IoRunner`. :data:`Step` names the pieces so that a retry can +resume at the one that failed rather than redoing the work before it. """ from __future__ import annotations @@ -38,7 +44,6 @@ from enum import Enum, auto from pathlib import Path from typing import Any, Protocol -from urllib.error import HTTPError, URLError from const import MODE_BIDIRECTIONAL, Actions from diffhandler import ( @@ -50,7 +55,7 @@ from gramps.gen.db import DbTxn from gramps.gen.db.utils import import_as_dict from gramps.gen.errors import HandleError -from webapihandler import transaction_to_json +from webapihandler import ServerTaskFailed, transaction_to_json LOG = logging.getLogger("grampswebsync") @@ -60,6 +65,9 @@ #: Server permission required to run a sync at all. REQUIRED_PERMISSION = "ViewPrivate" +#: A media transfer, resolved to a local path before it leaves the main loop. +Transfers = list[tuple[str, str, str]] + #: Stages reported through :meth:`SessionListener.on_status`. STATUS_FETCHING = "fetching" STATUS_COMPARING = "comparing" @@ -85,6 +93,21 @@ class State(Enum): FAILED = auto() +class Step(Enum): + """The resumable pieces of a run. + + A stage that both touches a database and talks to the network is two of + these, so that a retry after, say, a dropped connection while pushing does + not re-apply the local half that already succeeded. + """ + + FETCH = auto() # download the remote XML (network) + DIFF = auto() # import it and compare (database) + APPLY_LOCAL = auto() # write the local half (database) + PUSH_REMOTE = auto() # send the remote half (network) + TRANSFER = auto() # move media files (network) + + class ErrorKind(Enum): """Classification of a failure, independent of its localized wording.""" @@ -95,11 +118,13 @@ class ErrorKind(Enum): TREE_DISABLED = auto() # HTTP 503 CONFLICT = auto() # HTTP 409 SERVER_ERROR = auto() + SERVER_TASK_FAILED = auto() CONNECTION_FAILED = auto() INVALID_RESPONSE = auto() INSUFFICIENT_PERMISSIONS = auto() XML_IMPORT_FAILED = auto() APPLY_FAILED = auto() + STALE_LOCAL_DATA = auto() UNEXPECTED = auto() @@ -123,6 +148,10 @@ class ApplyFailed(Exception): """Applying the confirmed actions to the databases raised.""" +class StaleLocalData(Exception): + """The local tree changed between the comparison and the commit.""" + + # Login and mid-sync failures read a few status codes differently. _LOGIN_HTTP_ERRORS: dict[int, ErrorKind] = { 401: ErrorKind.AUTH_FAILED, @@ -140,7 +169,7 @@ class ApplyFailed(Exception): } -def classify_http_error(exc: HTTPError, *, login: bool) -> SyncError: +def classify_http_error(exc: Any, *, login: bool) -> SyncError: """Classify an :class:`HTTPError` into a :class:`SyncError`. :param exc: The raised error. @@ -184,7 +213,7 @@ def upload_media_file(self, handle: str, path: str) -> bool: ... class CredentialStore(Protocol): - """Persistence for server credentials and the last-sync timestamp.""" + """Persistence for server credentials and per-server sync baselines.""" def get_url(self) -> str: ... @@ -192,11 +221,13 @@ def get_username(self) -> str: ... def get_password(self) -> str | None: ... - def get_timestamp(self) -> float: ... + def get_timestamp(self, url: str, username: str) -> float: ... - def set_timestamp(self, timestamp: float) -> None: ... + def set_timestamp(self, url: str, username: str, timestamp: float) -> None: ... - def save_credentials(self, url: str, username: str, password: str) -> None: ... + def save_credentials( + self, url: str, username: str, password: str, remember_password: bool = True + ) -> None: ... class MediaStore(Protocol): @@ -217,6 +248,8 @@ def run( on_error: Callable[[BaseException], None], ) -> None: ... + def post(self, func: Callable[[], None]) -> None: ... + class Clock(Protocol): """Source of the current time.""" @@ -269,6 +302,16 @@ def next_state(state: State, session: SyncSession) -> State: return state +#: Which state a retry of each step returns to while it runs. +STATE_FOR_STEP: dict[Step, State] = { + Step.FETCH: State.COMPARING, + Step.DIFF: State.COMPARING, + Step.APPLY_LOCAL: State.APPLYING, + Step.PUSH_REMOTE: State.APPLYING, + Step.TRANSFER: State.TRANSFERRING, +} + + # ------------------------------------------------------------ # # SyncSession @@ -287,6 +330,7 @@ def __init__( runner: TaskRunner, clock: Clock, listener: SessionListener | None = None, + io_runner: TaskRunner | None = None, ) -> None: """Initialize the session. @@ -294,11 +338,13 @@ def __init__( :param user: A :class:`gramps.gen.user.User` for import/diff progress. :param backend_factory: Builds a :class:`Backend` from url, username and password. - :param credentials: Where credentials and the last-sync time live. + :param credentials: Where credentials and sync baselines live. :param media: Access to local media files. - :param runner: Executes the slow steps. + :param runner: Executes steps that touch a database, on the main loop. :param clock: Supplies the time recorded as the last successful sync. :param listener: Optional observer of state and progress. + :param io_runner: Executes network steps. Defaults to ``runner``, which + keeps everything on one thread -- useful in tests. """ self.db1 = db self.db2 = None @@ -307,6 +353,7 @@ def __init__( self.credentials = credentials self.media = media self.runner = runner + self.io_runner = io_runner if io_runner is not None else runner self.clock = clock self.listener = listener @@ -314,6 +361,13 @@ def __init__( self.error: SyncError | None = None #: Set when login fails. Recoverable, unlike :attr:`error`. self.login_error: SyncError | None = None + #: Which step failed, so :meth:`retry` can resume at the right place. + self.failed_in: Step | None = None + + self.url: str = "" + self.username: str = "" + self.password: str = "" + self.remember_password: bool = True self.backend: Backend | None = None self.sync: WebApiSyncDiffHandler | None = None @@ -323,9 +377,16 @@ def __init__( self.missing_local: list[tuple[str, str]] = [] self.missing_remote: list[tuple[str, str]] = [] + #: Media absent on both sides. Neither transfer can supply these, so + #: they are reported up front rather than as two failures each. + self.missing_both: list[tuple[str, str]] = [] self.downloaded: dict[str, bool] = {} self.uploaded: dict[str, bool] = {} + #: Held between the two halves of the apply stage, because a retry + #: after a failed push must not re-run the local commit. Everything + #: else a step hands to its successor travels as an argument. + self._payload: list[dict[str, Any]] | None = None self._closing = False # -------------------------------------------------------- @@ -333,7 +394,7 @@ def __init__( # -------------------------------------------------------- @property def has_missing_files(self) -> bool: - """Whether any media file is missing on either side.""" + """Whether any media file can actually be transferred either way.""" return bool(self.missing_local or self.missing_remote) @property @@ -346,6 +407,11 @@ def has_remote_actions(self) -> bool: """Whether the pending actions touch the remote database.""" return has_remote_actions(self.actions) + @property + def can_retry(self) -> bool: + """Whether :meth:`retry` has a step to resume.""" + return self.state is State.FAILED and self.failed_in is not None + # -------------------------------------------------------- # Internals # -------------------------------------------------------- @@ -360,10 +426,16 @@ def _advance(self) -> None: """Move to whatever :func:`next_state` says comes next.""" self._goto(next_state(self.state, self)) - def _fail(self, error: SyncError) -> None: - """Record a terminal failure and move to :attr:`State.FAILED`.""" + def _fail(self, error: SyncError, step: Step | None = None) -> None: + """Record a terminal failure and move to :attr:`State.FAILED`. + + The remote database is deliberately kept open: a retry that had to + re-download and re-diff it would throw away the most expensive part of + the run for what is usually a transient network problem. + """ LOG.warning("Sync failed: %s (%s)", error.kind.name, error.detail) self.error = error + self.failed_in = step self._goto(State.FAILED) def _progress(self, kind: str, fraction: float) -> None: @@ -376,10 +448,29 @@ def _status(self, stage: str) -> None: if self.listener is not None: self.listener.on_status(stage) + def _progress_from_worker(self, kind: str, fraction: float) -> None: + """Report progress raised inside a network step. + + Marshalled onto the main loop, since the listener draws widgets and the + step is running on a worker thread. + """ + self.io_runner.post(lambda: self._progress(kind, fraction)) + + def _status_from_worker(self, stage: str) -> None: + """Report a status update raised inside a network step.""" + self.io_runner.post(lambda: self._status(stage)) + def _classify(self, exc: BaseException, *, login: bool = False) -> SyncError: """Turn an exception raised by a port into a :class:`SyncError`.""" + # Imported here so the module stays importable without urllib present. + from urllib.error import HTTPError, URLError + if isinstance(exc, XmlImportFailed): return SyncError(ErrorKind.XML_IMPORT_FAILED) + if isinstance(exc, StaleLocalData): + return SyncError(ErrorKind.STALE_LOCAL_DATA) + if isinstance(exc, ServerTaskFailed): + return SyncError(ErrorKind.SERVER_TASK_FAILED, str(exc)) if isinstance(exc, ApplyFailed): return SyncError(ErrorKind.APPLY_FAILED, str(exc)) if isinstance(exc, HTTPError): @@ -391,6 +482,20 @@ def _classify(self, exc: BaseException, *, login: bool = False) -> SyncError: return SyncError(kind, str(exc)) return SyncError(ErrorKind.UNEXPECTED, str(exc)) + def _run(self, step: Step, func, on_success) -> None: + """Schedule ``step`` on the runner that is allowed to execute it. + + Network steps go to a worker thread; database steps stay on the main + loop, because a Gramps sqlite connection belongs to the thread that + created it. + """ + runner = ( + self.io_runner + if step in (Step.FETCH, Step.PUSH_REMOTE, Step.TRANSFER) + else self.runner + ) + runner.run(func, on_success, lambda exc: self._on_step_error(exc, step)) + # -------------------------------------------------------- # Intents # -------------------------------------------------------- @@ -398,18 +503,30 @@ def begin(self) -> None: """Leave the introduction page.""" self._advance() - def submit_credentials(self, url: str, username: str, password: str) -> None: + def submit_credentials( + self, + url: str, + username: str, + password: str, + remember_password: bool = True, + ) -> None: """Connect, authenticate, then download and diff the remote tree. On an authentication or permission problem the session stays on - :attr:`State.LOGIN` with :attr:`login_error` set. + :attr:`State.LOGIN` with :attr:`login_error` set. Credentials are stored + only once the server has accepted them, so a typo never reaches the + keyring. :param url: Server URL, already sanitized by the caller. :param username: Login name. :param password: Password. + :param remember_password: Whether the password may be stored. """ self.login_error = None - self.credentials.save_credentials(url, username, password) + self.url = url + self.username = username + self.password = password + self.remember_password = remember_password try: self.backend = self._backend_factory(url, username, password) @@ -425,8 +542,10 @@ def submit_credentials(self, url: str, username: str, password: str) -> None: self._goto(State.LOGIN) return - self._goto(State.COMPARING) - self.runner.run(self._compare, self._on_compared, self._on_step_error) + self.credentials.save_credentials( + url, username, password, remember_password + ) + self._start_compare() def confirm_changes(self, sync_mode: int) -> None: """Accept the reviewed changes and apply them. @@ -435,7 +554,7 @@ def confirm_changes(self, sync_mode: int) -> None: """ self.sync_mode = sync_mode self._goto(State.APPLYING) - self.runner.run(self._apply, self._on_applied, self._on_step_error) + self._run(Step.APPLY_LOCAL, self._apply_local, self._on_local_applied) def confirm_files(self) -> None: """Accept the media file transfer and carry it out. @@ -446,44 +565,99 @@ def confirm_files(self) -> None: self._advance() return self._goto(State.TRANSFERRING) - self.runner.run(self._transfer, self._on_transferred, self._on_step_error) + self._start_transfer() + + def retry(self) -> None: + """Resume a failed run at the step that failed. + + Everything before that step is left alone: the remote tree is still + downloaded and diffed, and a local commit that already succeeded is not + repeated. + """ + step = self.failed_in + if step is None: + return + LOG.info("Retrying sync from %s.", step.name) + self.error = None + self.failed_in = None + self._goto(STATE_FOR_STEP[step]) + if step is Step.FETCH: + self._run(Step.FETCH, self._fetch_xml, self._on_fetched) + elif step is Step.DIFF: + self._start_compare() + elif step is Step.APPLY_LOCAL: + self._run(Step.APPLY_LOCAL, self._apply_local, self._on_local_applied) + elif step is Step.PUSH_REMOTE: + self._run(Step.PUSH_REMOTE, self._push_remote, self._on_applied) + elif step is Step.TRANSFER: + self._start_transfer() def cancel(self) -> None: """Abandon the run and release the in-memory remote database.""" self._closing = True + self._release_remote() + + def _release_remote(self) -> None: + """Close the downloaded remote database, if one is open.""" if self.db2 is not None: self.db2.close() self.db2 = None self.sync = None # holds references to both databases # -------------------------------------------------------- - # Steps + # Comparison # -------------------------------------------------------- - def _on_step_error(self, exc: BaseException) -> None: - """Handle an exception escaping one of the background steps.""" - self._fail(self._classify(exc)) + def _start_compare(self) -> None: + """Enter the comparison stage and fetch the remote tree.""" + self._goto(State.COMPARING) + self._run(Step.FETCH, self._fetch_xml, self._on_fetched) + + def _fetch_xml(self) -> Path | None: + """Download the remote tree as Gramps XML. Network only. - def _compare(self) -> None: - """Download the remote tree and diff it against the local one.""" + :returns: Where the export was written, for the next step. + """ if self._closing: - return + return None assert self.backend is not None LOG.info("Downloading Gramps XML file.") - self._status(STATUS_FETCHING) + self._status_from_worker(STATUS_FETCHING) path = self.backend.download_xml() LOG.debug("Downloaded XML to %s", path) + return path + + def _on_fetched(self, path: Path | None) -> None: + """Import and diff, back on the main loop.""" + if self._closing or path is None: + return + self._run( + Step.DIFF, lambda: self._import_and_diff(path), self._on_compared + ) + + def _import_and_diff(self, path: Path) -> None: + """Import the downloaded XML and diff it against the local tree. + + Runs on the main loop: ``import_as_dict`` builds an in-memory sqlite + database on the calling thread, and ``diff_dbs`` reads the local one, + which belongs to the main thread. + :param path: The export downloaded by :meth:`_fetch_xml`. It is + consumed here, which is why retrying this step downloads again. + """ + if self._closing: + return try: db2 = import_as_dict(str(path), self._user) finally: - path.unlink() + path.unlink(missing_ok=True) if db2 is None: raise XmlImportFailed() + self._release_remote() self.db2 = db2 LOG.info("Comparing local and remote data.") self._status(STATUS_COMPARING) - last_synced = self.credentials.get_timestamp() or None + last_synced = self.credentials.get_timestamp(self.url, self.username) or None self.sync = WebApiSyncDiffHandler( self.db1, self.db2, user=self._user, last_synced=last_synced ) @@ -495,70 +669,204 @@ def _on_compared(self, _result: Any) -> None: return if not self.changes: LOG.info("Databases are in sync.") - self.credentials.set_timestamp(self.clock.now()) - self.missing_local = self._find_missing_local() - self.missing_remote = self._find_missing_remote() + self.credentials.set_timestamp(self.url, self.username, self.clock.now()) + self._scan_media() self._advance() - def _apply(self) -> None: - """Apply the confirmed actions locally, then push them to the server.""" + # -------------------------------------------------------- + # Applying + # -------------------------------------------------------- + def _apply_local(self) -> None: + """Write the local half of the sync and build the remote payload. + + Runs on the main loop: both halves are prepared inside Gramps + transactions against databases owned by this thread. + """ if self._closing: return assert self.backend is not None and self.sync is not None self.actions = changes_to_actions(self.changes, self.sync_mode) + self._payload = [] if not self.actions: return + self._assert_local_unchanged() + LOG.info("Committing %s actions.", len(self.actions)) try: with DbTxn(TXN_MSG, self.sync.db1) as trans1: with DbTxn(TXN_MSG, self.sync.db2) as trans2: self.sync.commit_actions(self.actions, trans1, trans2) lang = self.backend.get_lang() - payload = transaction_to_json(trans2, lang) + self._payload = transaction_to_json(trans2, lang) + except StaleLocalData: + raise except Exception as exc: raise ApplyFailed(str(exc)) from exc if self.has_local_actions: self._status(STATUS_LOCAL_APPLIED) + def _assert_local_unchanged(self) -> None: + """Verify the local tree still matches what the comparison saw. + + The comparison captured object snapshots, and the user may have gone on + editing the tree while reviewing them -- the tool does not block the + main window. Committing those snapshots would silently overwrite any + edit made in between, so the run stops instead and re-compares. + + :raises StaleLocalData: If any affected object changed or appeared. + """ + for _typ, handle, obj_type, obj1, _obj2 in self.actions: + method = self.db1.method("get_%s_from_handle", obj_type) + if method is None: + continue + try: + current = method(handle) + except HandleError: + current = None + if obj1 is None: + # Absent locally when compared; anything here now is new. + if current is not None: + raise StaleLocalData(f"{obj_type} {handle} was added locally") + elif current is None: + raise StaleLocalData(f"{obj_type} {handle} was deleted locally") + elif current.change != obj1.change: + raise StaleLocalData(f"{obj_type} {handle} was modified locally") + + def _on_local_applied(self, _result: Any) -> None: + """Push the remote half, off the main loop.""" + if self._closing: + return + self._run(Step.PUSH_REMOTE, self._push_remote, self._on_applied) + + def _push_remote(self) -> None: + """Send the remote half of the sync to the server. Network only.""" + if self._closing: + return + assert self.backend is not None + if not self._payload: + return # Always force: the server compares against the XML-round-tripped # object, which differs from the live one through serialization # artifacts alone, yielding spurious 409s. self.backend.commit( - payload, True, lambda fraction: self._progress("api", fraction) + self._payload, + True, + lambda fraction: self._progress_from_worker("api", fraction), ) + self._payload = [] def _on_applied(self, _result: Any) -> None: """Record the sync time and collect media state.""" if self._closing: return - self.credentials.set_timestamp(self.clock.now()) - self.missing_local = self._find_missing_local() - self.missing_remote = self._find_missing_remote() + self.credentials.set_timestamp(self.url, self.username, self.clock.now()) + self._scan_media() self._advance() - def _transfer(self) -> None: - """Download media missing locally, then upload media missing remotely. + # -------------------------------------------------------- + # Media + # -------------------------------------------------------- + def _scan_media(self) -> None: + """Work out which media files are missing, and on which side. - Progress reporting lets the view pump the event loop, so the user can - cancel mid-transfer; both loops check for that between files. + A file absent from both sides cannot be transferred in either + direction, so it is separated out here rather than being attempted + twice and reported as two failures. + """ + assert self.backend is not None + local_missing = { + media.handle: media.gramps_id + for media in self.db1.iter_media() + if not self.media.exists(media) + } + remote_missing = { + media["handle"]: media["gramps_id"] + for media in self.backend.get_missing_files() or [] + } + both = set(local_missing) & set(remote_missing) + + self.missing_both = [(local_missing[h], h) for h in both] + self.missing_local = [ + (gid, h) for h, gid in local_missing.items() if h not in both + ] + self.missing_remote = [ + (gid, h) for h, gid in remote_missing.items() if h not in both + ] + if self.missing_both: + LOG.warning( + "%s media file(s) are missing on both sides.", len(self.missing_both) + ) + + def _resolve_transfers(self) -> tuple[Transfers, Transfers]: + """Turn the missing-file lists into paths, on the main loop. + + The transfer itself runs on a worker thread and must not touch the + local database, so every handle is resolved to a path first. Files a + previous attempt already moved are left out, so a retry after a dropped + connection resumes rather than starting over. + + :returns: The downloads and uploads still to do. + """ + downloads: Transfers = [] + uploads: Transfers = [] + for gramps_id, handle in self.missing_local: + path = self._path_for(handle) + if path is None: + self.downloaded[gramps_id] = False + elif not self.downloaded.get(gramps_id): + downloads.append((gramps_id, handle, path)) + for gramps_id, handle in self.missing_remote: + path = self._path_for(handle) + if path is None: + self.uploaded[gramps_id] = False + elif not self.uploaded.get(gramps_id): + uploads.append((gramps_id, handle, path)) + return downloads, uploads + + def _start_transfer(self) -> None: + """Resolve paths on the main loop, then transfer off it.""" + downloads, uploads = self._resolve_transfers() + self._run( + Step.TRANSFER, + lambda: self._transfer(downloads, uploads), + self._on_transferred, + ) + + def _path_for(self, handle: str) -> str | None: + """Return the local path of a media object, or ``None`` if unusable.""" + try: + obj = self.db1.get_media_from_handle(handle) + except HandleError: + LOG.warning("Cannot access media object %s", handle) + return None + return self.media.full_path(obj) + + def _transfer(self, downloads: Transfers, uploads: Transfers) -> None: + """Download then upload media files. Network only. + + Both loops check for cancellation between files, so closing the window + stops the transfer rather than waiting for it to finish. + + :param downloads: Files to fetch, as ``(gramps_id, handle, path)``. + :param uploads: Files to send, in the same form. """ if self._closing: return assert self.backend is not None - for gramps_id, handle in self.missing_local: + for index, (gramps_id, handle, path) in enumerate(downloads, start=1): if self._closing: return LOG.debug("Downloading file %s", gramps_id) - self.downloaded[gramps_id] = self._download_one(handle) - self._progress("download", len(self.downloaded) / len(self.missing_local)) - for gramps_id, handle in self.missing_remote: + self.downloaded[gramps_id] = self._download_one(handle, path) + self._progress_from_worker("download", index / len(downloads)) + for index, (gramps_id, handle, path) in enumerate(uploads, start=1): if self._closing: return LOG.debug("Uploading file %s", gramps_id) - self.uploaded[gramps_id] = self._upload_one(handle) - self._progress("upload", len(self.uploaded) / len(self.missing_remote)) + self.uploaded[gramps_id] = self._upload_one(handle, path) + self._progress_from_worker("upload", index / len(uploads)) def _on_transferred(self, _result: Any) -> None: """Finish the run.""" @@ -566,62 +874,38 @@ def _on_transferred(self, _result: Any) -> None: return self._advance() - # -------------------------------------------------------- - # Media helpers - # -------------------------------------------------------- - def _find_missing_local(self) -> list[tuple[str, str]]: - """Return ``(gramps_id, handle)`` for media whose file is absent locally.""" - return [ - (media.gramps_id, media.handle) - for media in self.db1.iter_media() - if not self.media.exists(media) - ] - - def _find_missing_remote(self) -> list[tuple[str, str]]: - """Return ``(gramps_id, handle)`` for media whose file is absent remotely.""" - assert self.backend is not None - return [ - (media["gramps_id"], media["handle"]) - for media in self.backend.get_missing_files() or [] - ] - - def _download_one(self, handle: str) -> bool: + def _download_one(self, handle: str, path: str) -> bool: """Download one media file, reporting failure rather than raising.""" assert self.backend is not None try: - obj = self.db1.get_media_from_handle(handle) - except HandleError: - LOG.warning("Cannot access media object %s", handle) - return False - try: - return self.backend.download_media_file(handle, self.media.full_path(obj)) + return self.backend.download_media_file(handle, path) except Exception as exc: # noqa: BLE001 -- one bad file must not abort - LOG.warning("Failed to download media file %s: %s", obj.gramps_id, exc) + LOG.warning("Failed to download media file %s: %s", handle, exc) return False - def _upload_one(self, handle: str) -> bool: + def _upload_one(self, handle: str, path: str) -> bool: """Upload one media file. - A file absent on both sides appears in both missing lists: the - download cannot supply it and there is nothing local to send, so it is - recorded as a failure instead of raising. - :param handle: Handle of the media object to upload. + :param path: Where its file lives locally. :returns: Whether the file was uploaded. """ + import os + assert self.backend is not None - try: - obj = self.db1.get_media_from_handle(handle) - except HandleError: - LOG.warning("Cannot access media object %s", handle) + if not os.path.exists(path): + LOG.warning("Cannot upload media file %s: not on disk (%s)", handle, path) return False - if not self.media.exists(obj): - LOG.warning( - "Cannot upload media file %s: missing locally as well (%s)", - obj.gramps_id, - self.media.full_path(obj), - ) - return False - return self.backend.upload_media_file(handle, self.media.full_path(obj)) - + return self.backend.upload_media_file(handle, path) + # -------------------------------------------------------- + # Step failure + # -------------------------------------------------------- + def _on_step_error(self, exc: BaseException, step: Step) -> None: + """Handle an exception escaping one of the steps.""" + if self._closing: + return + # Stale local data means the snapshots are worthless; a retry has to + # compare again rather than resume where it stopped. + resume = Step.DIFF if isinstance(exc, StaleLocalData) else step + self._fail(self._classify(exc), resume) diff --git a/GrampsWebSync/tests/fakes.py b/GrampsWebSync/tests/fakes.py index 9937c247b..7cf71977f 100644 --- a/GrampsWebSync/tests/fakes.py +++ b/GrampsWebSync/tests/fakes.py @@ -235,7 +235,8 @@ class InlineTaskRunner: """Runs each task synchronously on the calling thread. By the time ``run`` returns, the step and its completion callback have - both finished. + both finished. Standing in for both runners keeps scenarios single-threaded + and their assertions deterministic. """ def run( @@ -252,6 +253,10 @@ def run( else: on_success(result) + def post(self, func: Callable[[], None]) -> None: + """Run ``func`` immediately; there is no other thread to marshal from.""" + func() + class FrozenClock: """A clock that only moves when :meth:`advance` is called. @@ -274,6 +279,9 @@ def advance(self, seconds: float) -> None: class MemoryCredentialStore: """In-memory stand-in for the config file and keyring. + Keyed by ``(url, username)`` like the real store, so each server keeps its + own sync baseline and switching between them does not discard one. + :param url: Initially stored server URL. :param username: Initially stored user name. :param password: Initially stored password. @@ -290,11 +298,23 @@ def __init__( self.url = url self.username = username self.password = password - self.timestamp = timestamp + #: ``(url, username)`` -> last successful sync time. + self.timestamps: dict[tuple[str, str], float] = {(url, username): timestamp} + #: ``(url, username)`` -> whether its password may be stored. + self.remembered: dict[tuple[str, str], bool] = {} #: Every ``(url, username, password)`` passed to #: :meth:`save_credentials`. self.saved: list[tuple[str, str, str]] = [] + @property + def timestamp(self) -> float: + """The baseline of the last-used entry, for convenient assertions.""" + return self.timestamps.get((self.url, self.username), 0.0) + + @timestamp.setter + def timestamp(self, value: float) -> None: + self.timestamps[(self.url, self.username)] = value + def get_url(self) -> str: return self.url @@ -304,19 +324,22 @@ def get_username(self) -> str: def get_password(self) -> str | None: return self.password - def get_timestamp(self) -> float: - return self.timestamp + def get_timestamp(self, url: str, username: str) -> float: + return self.timestamps.get((url, username), 0.0) - def set_timestamp(self, timestamp: float) -> None: - self.timestamp = timestamp + def set_timestamp(self, url: str, username: str, timestamp: float) -> None: + self.timestamps[(url, username)] = timestamp + self.url = url + self.username = username - def save_credentials(self, url: str, username: str, password: str) -> None: - # A changed URL invalidates the last-sync time, as in production. - if url != self.url: - self.timestamp = 0.0 + def save_credentials( + self, url: str, username: str, password: str, remember_password: bool = True + ) -> None: self.url = url self.username = username - self.password = password + self.password = password if remember_password else None + self.remembered[(url, username)] = remember_password + self.timestamps.setdefault((url, username), 0.0) self.saved.append((url, username, password)) diff --git a/GrampsWebSync/tests/scenario.py b/GrampsWebSync/tests/scenario.py index bdf612ded..516b28957 100644 --- a/GrampsWebSync/tests/scenario.py +++ b/GrampsWebSync/tests/scenario.py @@ -58,6 +58,11 @@ RecordingListener, ) +#: The server a scenario authenticates against unless told otherwise. Named so +#: that tests asserting on a per-server baseline can name the same entry. +DEFAULT_URL = "https://example.org/api" +DEFAULT_USERNAME = "owner" + #: A convenient baseline "already synced" time for scenarios. T0 = 1_600_000_000.0 #: A time after :data:`T0`, for an edit on one side. @@ -348,8 +353,8 @@ def run( self, mode: int = MODE_BIDIRECTIONAL, confirm_files: bool = True, - url: str = "https://example.org/api", - username: str = "owner", + url: str = DEFAULT_URL, + username: str = DEFAULT_USERNAME, password: str = "secret", ) -> RunResult: """Drive a complete sync, answering every confirmation. diff --git a/GrampsWebSync/tests/test_adapters.py b/GrampsWebSync/tests/test_adapters.py index 4e938bb73..2f3ae8365 100644 --- a/GrampsWebSync/tests/test_adapters.py +++ b/GrampsWebSync/tests/test_adapters.py @@ -27,35 +27,39 @@ import threading import unittest -from adapters import GLibTaskRunner +from adapters import GLibTaskRunner, IoRunner from gi.repository import GLib #: Milliseconds before an unresponsive loop is torn down. TIMEOUT_MS = 5000 -def run_task(func): - """Run ``func`` through :class:`GLibTaskRunner` and return the outcome. +def run_task(func, runner=None): + """Run ``func`` through a runner and return the outcome. :param func: The task to schedule. - :returns: Dict with ``result`` or ``error``, and ``thread``. + :param runner: The runner to use. Defaults to :class:`GLibTaskRunner`. + :returns: Dict with ``result`` or ``error``, ``thread`` and + ``callback_thread``. """ outcome: dict = {} loop = GLib.MainLoop() def on_success(result): outcome["result"] = result + outcome["callback_thread"] = threading.current_thread() loop.quit() def on_error(exc): outcome["error"] = exc + outcome["callback_thread"] = threading.current_thread() loop.quit() def wrapped(): outcome["thread"] = threading.current_thread() return func() - GLibTaskRunner().run(wrapped, on_success, on_error) + (runner or GLibTaskRunner()).run(wrapped, on_success, on_error) GLib.timeout_add(TIMEOUT_MS, loop.quit) loop.run() return outcome @@ -90,5 +94,43 @@ def test_task_is_run_exactly_once(self) -> None: self.assertEqual(len(calls), 1) +class IoRunnerTest(unittest.TestCase): + """Network steps leave the main loop, but their callbacks come back to it.""" + + def test_task_runs_off_the_calling_thread(self) -> None: + """This is what keeps the window responsive while a request is in + flight, and what makes Cancel work at all.""" + outcome = run_task(lambda: "done", runner=IoRunner()) + self.assertEqual(outcome.get("result"), "done") + self.assertIsNot(outcome["thread"], threading.current_thread()) + + def test_callback_returns_to_the_main_loop(self) -> None: + """Listeners draw widgets, so they must not run on the worker.""" + outcome = run_task(lambda: "done", runner=IoRunner()) + self.assertIs(outcome["callback_thread"], threading.current_thread()) + + def test_failure_is_reported_to_the_error_callback(self) -> None: + def boom(): + raise ValueError("boom") + + outcome = run_task(boom, runner=IoRunner()) + self.assertNotIn("result", outcome) + self.assertIsInstance(outcome.get("error"), ValueError) + + def test_post_runs_on_the_main_loop(self) -> None: + """Progress raised inside a network step is marshalled through this.""" + seen: dict = {} + loop = GLib.MainLoop() + + def note(): + seen["thread"] = threading.current_thread() + loop.quit() + + threading.Thread(target=lambda: IoRunner().post(note)).start() + GLib.timeout_add(TIMEOUT_MS, loop.quit) + loop.run() + self.assertIs(seen.get("thread"), threading.current_thread()) + + if __name__ == "__main__": unittest.main() diff --git a/GrampsWebSync/tests/test_credentials.py b/GrampsWebSync/tests/test_credentials.py new file mode 100644 index 000000000..8a6acbcdd --- /dev/null +++ b/GrampsWebSync/tests/test_credentials.py @@ -0,0 +1,386 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Tests for :class:`adapters.ConfigCredentialStore` and its keyring guard. + +Every store is built against a config manager in a temporary directory, so no +test can write to the user's own Gramps configuration. +""" + +from __future__ import annotations + +import itertools +import shutil +import tempfile +import unittest +import unittest.mock +from typing import cast + +from adapters import ( + LEGACY_TIMESTAMP, + LEGACY_URL, + LEGACY_USERNAME, + ConfigCredentialStore, + Keyring, + normalize_url, + snap_connect_command, +) +from gramps.gen.config import config as configman + +URL = "https://example.org/api" +OTHER = "https://other.example/api" + +#: Config managers are cached by name, so each store needs its own. +_counter = itertools.count() + + +class FakeKeyring: + """A keyring that records calls and can be made to fail.""" + + def __init__(self, fail: Exception | None = None) -> None: + self.stored: dict[tuple[str, str], str] = {} + self.deleted: list[tuple[str, str]] = [] + self.unavailable = None + self._fail = fail + + def get(self, service, username): + return self.stored.get((service, username)) + + def set(self, service, username, password): + if self._fail is not None: + self.unavailable = self._fail + return False + self.stored[(service, username)] = password + return True + + def delete(self, service, username): + self.deleted.append((service, username)) + self.stored.pop((service, username), None) + + +class StoreTestCase(unittest.TestCase): + """Builds isolated stores.""" + + def setUp(self) -> None: + self.tmpdir = tempfile.mkdtemp(prefix="gws_config_") + self.addCleanup(shutil.rmtree, self.tmpdir, ignore_errors=True) + + def make_config(self, name: str | None = None): + """Return a config manager writing into this test's directory.""" + name = name or f"webapisync_test_{next(_counter)}" + return configman.register_manager( + name, self.tmpdir, use_plugins_path=False + ) + + def make_store(self, config=None, keyring=None) -> ConfigCredentialStore: + """Return a store over an isolated config manager.""" + return ConfigCredentialStore( + keyring=cast(Keyring, keyring or FakeKeyring()), + config=config or self.make_config(), + ) + + +class NormalizeUrlTest(unittest.TestCase): + """The key has to survive the ways people type a URL.""" + + def test_trailing_slash_and_whitespace_are_ignored(self) -> None: + """These used to look like different servers and cost the baseline.""" + self.assertEqual(normalize_url(" https://x.org/ "), "https://x.org") + self.assertEqual(normalize_url("https://x.org///"), "https://x.org") + + +class PerServerBaselineTest(StoreTestCase): + """Each server keeps its own last-sync time.""" + + def test_two_servers_do_not_share_a_baseline(self) -> None: + store = self.make_store() + store.set_timestamp(URL, "owner", 111.0) + store.set_timestamp(OTHER, "owner", 222.0) + self.assertEqual(store.get_timestamp(URL, "owner"), 111.0) + self.assertEqual(store.get_timestamp(OTHER, "owner"), 222.0) + + def test_same_server_different_users_are_separate_trees(self) -> None: + """A Gramps Web account maps to one tree, so the user is part of the key.""" + store = self.make_store() + store.set_timestamp(URL, "alice", 111.0) + store.set_timestamp(URL, "bob", 222.0) + self.assertEqual(store.get_timestamp(URL, "alice"), 111.0) + + def test_a_trailing_slash_does_not_lose_the_baseline(self) -> None: + store = self.make_store() + store.set_timestamp(URL, "owner", 111.0) + self.assertEqual(store.get_timestamp(URL + "/", "owner"), 111.0) + + def test_an_unknown_server_has_no_baseline(self) -> None: + store = self.make_store() + self.assertEqual(store.get_timestamp("https://new.example", "owner"), 0.0) + + +class MigrationTest(StoreTestCase): + """Upgrading from the pre-multi-server layout must lose nothing.""" + + def test_legacy_keys_become_an_entry(self) -> None: + config = self.make_config() + config.register(LEGACY_URL, "") + config.register(LEGACY_USERNAME, "") + config.register(LEGACY_TIMESTAMP, 0) + config.set(LEGACY_URL, URL) + config.set(LEGACY_USERNAME, "owner") + config.set(LEGACY_TIMESTAMP, 999) + + store = self.make_store(config=config) + + self.assertEqual(store.get_url(), URL) + self.assertEqual(store.get_username(), "owner") + self.assertEqual(store.get_timestamp(URL, "owner"), 999.0) + + def test_migration_preserves_the_baseline(self) -> None: + """Losing it would make the first run after upgrading a cold sync.""" + config = self.make_config() + for key, value in ( + (LEGACY_URL, URL), + (LEGACY_USERNAME, "owner"), + (LEGACY_TIMESTAMP, 4242), + ): + config.register(key, "" if isinstance(value, str) else 0) + config.set(key, value) + store = self.make_store(config=config) + self.assertNotEqual(store.get_timestamp(URL, "owner"), 0.0) + + def test_nothing_stored_migrates_to_nothing(self) -> None: + store = self.make_store() + self.assertEqual(store.get_url(), "") + self.assertEqual(store.get_username(), "") + + +class LegacyMirrorTest(StoreTestCase): + """The old keys stay current so a downgrade still works.""" + + def test_saving_mirrors_into_the_legacy_keys(self) -> None: + config = self.make_config() + store = self.make_store(config=config) + store.save_credentials(URL, "owner", "secret") + store.set_timestamp(URL, "owner", 777.0) + + self.assertEqual(config.get(LEGACY_URL), URL) + self.assertEqual(config.get(LEGACY_USERNAME), "owner") + self.assertEqual(config.get(LEGACY_TIMESTAMP), 777) + + def test_a_newer_legacy_baseline_wins_on_re_upgrade(self) -> None: + """An older version may have synced while it was installed.""" + config = self.make_config() + store = self.make_store(config=config) + store.set_timestamp(URL, "owner", 100.0) + # Stand in for an older version syncing and writing only its own keys. + config.set(LEGACY_TIMESTAMP, 500) + config.save() + + reopened = self.make_store(config=config) + + self.assertEqual(reopened.get_timestamp(URL, "owner"), 500.0) + + def test_an_older_legacy_baseline_does_not_regress_the_entry(self) -> None: + config = self.make_config() + store = self.make_store(config=config) + store.set_timestamp(URL, "owner", 500.0) + config.set(LEGACY_TIMESTAMP, 100) + config.save() + + reopened = self.make_store(config=config) + + self.assertEqual(reopened.get_timestamp(URL, "owner"), 500.0) + + def test_an_unreadable_server_list_is_treated_as_empty(self) -> None: + """A value the config manager could not parse is stored as None. + + Registering a default does not help: defaults apply only when a key is + absent, not when it is present and None, so the store has to check the + type rather than assume it. Written to the file directly because + ``set`` type-checks and would reject it. + """ + config = self.make_config() + with open(config.filename, "w", encoding="utf-8") as fobj: + fobj.write("[credentials]\nservers=<< None: + keyring = FakeKeyring() + store = self.make_store(keyring=keyring) + store.save_credentials(URL, "owner", "secret", remember_password=True) + self.assertEqual(keyring.stored[(URL, "owner")], "secret") + + def test_declining_deletes_rather_than_merely_skipping(self) -> None: + """Otherwise the setting appears inert for anyone who had it on.""" + keyring = FakeKeyring() + store = self.make_store(keyring=keyring) + store.save_credentials(URL, "owner", "secret", remember_password=True) + + store.save_credentials(URL, "owner", "secret", remember_password=False) + + self.assertNotIn((URL, "owner"), keyring.stored) + self.assertIn((URL, "owner"), keyring.deleted) + + def test_the_entry_survives_even_when_the_password_does_not(self) -> None: + """The baseline is not a credential; dropping it would force cold syncs.""" + store = self.make_store() + store.set_timestamp(URL, "owner", 321.0) + store.save_credentials(URL, "owner", "secret", remember_password=False) + self.assertEqual(store.get_timestamp(URL, "owner"), 321.0) + + def test_an_unremembered_password_is_not_returned(self) -> None: + store = self.make_store() + store.save_credentials(URL, "owner", "secret", remember_password=False) + self.assertIsNone(store.get_password()) + + +class ForgetTest(StoreTestCase): + """Forgetting is the wider case of the same delete.""" + + def test_forget_removes_entry_keyring_and_mirror(self) -> None: + config = self.make_config() + keyring = FakeKeyring() + store = self.make_store(config=config, keyring=keyring) + store.save_credentials(URL, "owner", "secret") + + store.forget(URL, "owner") + + self.assertEqual(store.get_url(), "") + self.assertEqual(store.get_timestamp(URL, "owner"), 0.0) + self.assertIn((URL, "owner"), keyring.deleted) + self.assertEqual(config.get(LEGACY_URL), "") + + def test_forget_leaves_other_servers_alone(self) -> None: + store = self.make_store() + store.set_timestamp(URL, "owner", 111.0) + store.set_timestamp(OTHER, "owner", 222.0) + + store.forget(URL, "owner") + + self.assertEqual(store.get_timestamp(OTHER, "owner"), 222.0) + + +class ExplodingBackend: + """A keyring backend that raises, as one does under snap confinement.""" + + def __init__(self, exc: Exception) -> None: + self.exc = exc + self.calls: list[str] = [] + + def get_password(self, *_args): + self.calls.append("get") + raise self.exc + + def set_password(self, *_args): + self.calls.append("set") + raise self.exc + + def delete_password(self, *_args): + self.calls.append("delete") + raise self.exc + + +class KeyringOverBackend(Keyring): + """The real guard logic over a backend the test controls.""" + + def __init__(self, backend: ExplodingBackend) -> None: + super().__init__() + self.backend = backend + + def _module(self): + return None if self.unavailable is not None else self.backend + + +class KeyringGuardTest(unittest.TestCase): + """A broken keyring must not take Gramps down with it.""" + + def test_a_backend_raising_is_reported_not_propagated(self) -> None: + """Under snap confinement this arrives as a jeepney DBusErrorResponse. + + That does not derive from ``keyring.errors``, because it comes from a + transitive dependency of the backend, so guarding on the keyring + package's own exception hierarchy would not catch it. + """ + + class DBusErrorResponse(Exception): + pass + + keyring = KeyringOverBackend( + ExplodingBackend(DBusErrorResponse("An AppArmor policy prevents...")) + ) + + self.assertIsNone(keyring.get("svc", "user")) + problem = keyring.unavailable + self.assertIsNotNone(problem) + assert problem is not None # for the type checker + self.assertIn("AppArmor", problem.detail) + + def test_a_write_failure_is_reported_as_not_stored(self) -> None: + keyring = KeyringOverBackend(ExplodingBackend(RuntimeError("denied"))) + self.assertFalse(keyring.set("svc", "user", "pw")) + self.assertIsNotNone(keyring.unavailable) + + def test_a_failure_stops_further_attempts(self) -> None: + """One denial is enough; retrying each call just repeats the stall.""" + backend = ExplodingBackend(RuntimeError("denied")) + keyring = KeyringOverBackend(backend) + + keyring.set("svc", "user", "pw") + keyring.set("svc", "user", "pw") + keyring.get("svc", "user") + + self.assertEqual(backend.calls, ["set"]) + + def test_deleting_a_missing_entry_is_not_a_failure(self) -> None: + """Backends raise when asked to delete something that is not there.""" + keyring = KeyringOverBackend(ExplodingBackend(RuntimeError("no such item"))) + keyring.delete("svc", "nobody") + self.assertIsNone(keyring.unavailable) + + +class SnapHintTest(unittest.TestCase): + """Under snap the failure is a setting the user can change.""" + + def test_no_command_outside_snap(self) -> None: + with unittest.mock.patch.dict("os.environ", {}, clear=True): + self.assertIsNone(snap_connect_command()) + + def test_the_instance_name_is_used_when_present(self) -> None: + """A parallel install is named gramps_foo, and the command must match.""" + env = {"SNAP": "/snap/gramps/11", "SNAP_INSTANCE_NAME": "gramps_beta"} + with unittest.mock.patch.dict("os.environ", env, clear=True): + command = snap_connect_command() or "" + self.assertIn("gramps_beta:password-manager-service", command) + + def test_it_falls_back_to_the_snap_name(self) -> None: + env = {"SNAP": "/snap/gramps/11", "SNAP_NAME": "gramps"} + with unittest.mock.patch.dict("os.environ", env, clear=True): + self.assertEqual( + snap_connect_command(), "snap connect gramps:password-manager-service" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_errors.py b/GrampsWebSync/tests/test_errors.py index 9beea27f5..ea32a840b 100644 --- a/GrampsWebSync/tests/test_errors.py +++ b/GrampsWebSync/tests/test_errors.py @@ -26,7 +26,13 @@ from session import ErrorKind, State from .fakes import http_error -from .scenario import T0, T2, SyncScenario +from .scenario import ( + DEFAULT_URL as URL, + DEFAULT_USERNAME as USERNAME, + T0, + T2, + SyncScenario, +) class LoginFailureTest(unittest.TestCase): @@ -142,12 +148,13 @@ def test_failed_run_does_not_record_a_sync_timestamp(self) -> None: scenario.run() - self.assertEqual(scenario.credentials.get_timestamp(), T0) + self.assertEqual(scenario.credentials.get_timestamp(URL, USERNAME), T0) def test_local_changes_survive_a_failed_remote_commit(self) -> None: - """The local transaction commits before the upload is attempted.""" + """The local half commits before the remote half is even attempted.""" scenario = self.make_scenario() scenario.remote.edit_person("I0002", surname="Neu", changed_at=T2) + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) scenario.server.fail_always("commit", http_error(500)) result = scenario.run() @@ -172,7 +179,7 @@ def test_cancel_before_compare_skips_the_download(self) -> None: session.begin() session.cancel() - session._compare() + session._fetch_xml() self.assertNotIn("download_xml", scenario.server.calls) @@ -185,7 +192,7 @@ def test_cancel_before_apply_sends_nothing(self) -> None: session.submit_credentials("https://example.org/api", "owner", "secret") session.cancel() - session._apply() + session._apply_local() self.assertEqual(scenario.server.committed, []) @@ -199,7 +206,7 @@ def test_cancel_before_transfer_moves_no_files(self) -> None: session.submit_credentials("https://example.org/api", "owner", "secret") session.cancel() - session._transfer() + session._transfer(*session._resolve_transfers()) self.assertEqual(scenario.server.media_files, {}) @@ -229,11 +236,12 @@ def test_failed_download_is_recorded_without_failing_the_run(self) -> None: self.assertIs(result.final_state, State.DONE) self.assertEqual(result.session.downloaded, {"O0001": False, "O0002": True}) - def test_file_missing_on_both_sides_is_recorded_not_fatal(self) -> None: - """Such an object is in both missing lists; neither side can supply it. + def test_file_missing_on_both_sides_is_reported_once(self) -> None: + """Neither side can supply such a file, so neither transfer is tried. - The download 404s and the upload has nothing to send, so both are - recorded as failures and the run still completes. + It used to appear in both missing lists, so the download 404d and the + upload found nothing to send, and the user was told of two errors for + one file that simply does not exist anywhere. """ scenario = self.make_scenario() scenario.local.add_media("O0001", "nowhere.jpg", on_disk=False) @@ -243,8 +251,11 @@ def test_file_missing_on_both_sides_is_recorded_not_fatal(self) -> None: self.assertIs(result.final_state, State.DONE) self.assertIsNone(result.error) - self.assertEqual(result.session.downloaded, {"O0001": False}) - self.assertEqual(result.session.uploaded, {"O0001": False}) + self.assertEqual([gid for gid, _h in result.session.missing_both], ["O0001"]) + self.assertEqual(result.session.missing_local, []) + self.assertEqual(result.session.missing_remote, []) + self.assertEqual(result.session.downloaded, {}) + self.assertEqual(result.session.uploaded, {}) def test_upload_still_fails_the_run_on_a_server_error(self) -> None: """The new guard must not swallow genuine transport failures.""" diff --git a/GrampsWebSync/tests/test_recovery.py b/GrampsWebSync/tests/test_recovery.py new file mode 100644 index 000000000..523b43fa6 --- /dev/null +++ b/GrampsWebSync/tests/test_recovery.py @@ -0,0 +1,237 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Recovering from a failed run, and refusing to commit a stale comparison.""" + +from __future__ import annotations + +import unittest + +from const import MODE_BIDIRECTIONAL +from gramps.gen.db import DbTxn +from session import ErrorKind, State, Step, SyncSession + +from .fakes import http_error +from .scenario import ( + DEFAULT_URL as URL, + DEFAULT_USERNAME as USERNAME, + T0, + T2, + SyncScenario, +) + + +class RecoveryTestCase(unittest.TestCase): + """Drives a session by hand, so a run can be interrupted mid-flow.""" + + def make_scenario(self) -> SyncScenario: + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.seed_person("I0001", surname="Doe", changed_at=T0) + scenario.seed_person("I0002", surname="Roe", changed_at=T0) + scenario.share() + return scenario + + def connect(self, scenario: SyncScenario) -> SyncSession: + """Return a session that has connected and compared.""" + session = scenario.make_session() + session.begin() + session.submit_credentials(URL, USERNAME, "secret") + return session + + +class RetryTest(RecoveryTestCase): + """A failed run resumes where it stopped rather than starting over.""" + + def test_no_retry_is_offered_without_a_failure(self) -> None: + scenario = self.make_scenario() + session = self.connect(scenario) + self.assertFalse(session.can_retry) + + def test_a_failed_push_can_be_retried(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + scenario.server.fail_next("commit", http_error(500)) + session = self.connect(scenario) + + session.confirm_changes(MODE_BIDIRECTIONAL) + self.assertIs(session.state, State.FAILED) + self.assertIs(session.failed_in, Step.PUSH_REMOTE) + + session.retry() + + self.assertIsNone(session.error) + self.assertEqual(scenario.remote.surname("I0001"), "Mueller") + + def test_retrying_a_push_does_not_re_apply_the_local_half(self) -> None: + """Resuming at the failed step is the whole point of tracking it.""" + scenario = self.make_scenario() + scenario.remote.edit_person("I0002", surname="Neu", changed_at=T2) + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + scenario.server.fail_next("commit", http_error(500)) + session = self.connect(scenario) + + session.confirm_changes(MODE_BIDIRECTIONAL) + session.retry() + + # One payload reached the server: the retry re-sent, it did not stack a + # second local transaction on top of the first. + self.assertEqual(len(scenario.server.committed), 1) + self.assertEqual(scenario.local.surname("I0002"), "Neu") + + def test_retrying_a_push_does_not_download_the_tree_again(self) -> None: + """The remote database is kept open precisely so this is cheap.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + scenario.server.fail_next("commit", http_error(500)) + session = self.connect(scenario) + + session.confirm_changes(MODE_BIDIRECTIONAL) + session.retry() + + self.assertEqual(scenario.server.calls.count("download_xml"), 1) + + def test_a_failed_download_is_retried_from_the_start(self) -> None: + scenario = self.make_scenario() + scenario.server.fail_next("download_xml", http_error(500)) + session = scenario.make_session() + session.begin() + session.submit_credentials(URL, USERNAME, "secret") + + self.assertIs(session.state, State.FAILED) + self.assertIs(session.failed_in, Step.FETCH) + + session.retry() + + self.assertIsNone(session.error) + self.assertEqual(scenario.server.calls.count("download_xml"), 2) + + def test_a_failed_transfer_resumes_with_the_remaining_files(self) -> None: + """Files already moved are not sent twice.""" + scenario = SyncScenario() + self.addCleanup(scenario.close) + scenario.local.add_media("O0001", "one.jpg", changed_at=T0) + scenario.local.add_media("O0002", "two.jpg", changed_at=T0) + scenario.share() + session = self.connect(scenario) + self.assertIs(session.state, State.REVIEW_FILES) + + scenario.server.fail_next("upload_media_file", http_error(500)) + session.confirm_files() + self.assertIs(session.state, State.FAILED) + + session.retry() + + self.assertIs(session.state, State.DONE) + self.assertEqual(session.uploaded, {"O0001": True, "O0002": True}) + self.assertEqual(len(scenario.server.media_files), 2) + + def test_retry_without_a_recorded_step_does_nothing(self) -> None: + scenario = self.make_scenario() + session = self.connect(scenario) + session.failed_in = None + session.retry() # must not raise + self.assertIsNone(session.error) + + +class StaleComparisonTest(RecoveryTestCase): + """Edits made while the review page is open must not be overwritten. + + The comparison captures object snapshots and the tool does not block the + main window, so the user can keep editing. Committing those snapshots would + silently discard whatever they did in the meantime. + """ + + def test_an_edit_during_review_stops_the_commit(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + session = self.connect(scenario) + self.assertIs(session.state, State.REVIEW_CHANGES) + + scenario.local.edit_person("I0001", surname="Later", changed_at=T2 + 10) + session.confirm_changes(MODE_BIDIRECTIONAL) + + self.assertIs(session.state, State.FAILED) + self.assertIs(session.error.kind, ErrorKind.STALE_LOCAL_DATA) + + def test_nothing_is_sent_when_the_comparison_is_stale(self) -> None: + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + session = self.connect(scenario) + + scenario.local.edit_person("I0001", surname="Later", changed_at=T2 + 10) + session.confirm_changes(MODE_BIDIRECTIONAL) + + self.assertEqual(scenario.server.committed, []) + self.assertEqual(scenario.local.surname("I0001"), "Later") + + def test_a_deletion_during_review_is_caught(self) -> None: + """A delete is as destructive as an edit and must be caught too.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + session = self.connect(scenario) + + scenario.local.delete_person("I0001") + session.confirm_changes(MODE_BIDIRECTIONAL) + + self.assertIs(session.error.kind, ErrorKind.STALE_LOCAL_DATA) + + def test_an_object_appearing_locally_is_caught(self) -> None: + """The action was 'add here', which now would collide with real data.""" + scenario = self.make_scenario() + scenario.remote.add_person("I0003", surname="Nieuw", changed_at=T2) + session = self.connect(scenario) + self.assertIs(session.state, State.REVIEW_CHANGES) + + person = scenario.remote.person("I0003") + with DbTxn("local add", scenario.db1) as trans: + scenario.db1.add_person(person, trans) + + session.confirm_changes(MODE_BIDIRECTIONAL) + + self.assertIs(session.error.kind, ErrorKind.STALE_LOCAL_DATA) + + def test_retry_after_a_stale_comparison_compares_again(self) -> None: + """The snapshots are worthless, so resuming the commit is not an option.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + session = self.connect(scenario) + + scenario.local.edit_person("I0001", surname="Later", changed_at=T2 + 10) + session.confirm_changes(MODE_BIDIRECTIONAL) + self.assertIs(session.failed_in, Step.DIFF) + + session.retry() + + self.assertEqual(scenario.server.calls.count("download_xml"), 2) + self.assertIsNone(session.error) + + def test_an_untouched_tree_commits_normally(self) -> None: + """The guard must not fire on a run where nothing changed underneath.""" + scenario = self.make_scenario() + scenario.local.edit_person("I0001", surname="Mueller", changed_at=T2) + session = self.connect(scenario) + + session.confirm_changes(MODE_BIDIRECTIONAL) + + self.assertIsNone(session.error) + self.assertEqual(scenario.remote.surname("I0001"), "Mueller") + + +if __name__ == "__main__": + unittest.main() diff --git a/GrampsWebSync/tests/test_sync_flow.py b/GrampsWebSync/tests/test_sync_flow.py index 60b6f2fd6..c5502bb93 100644 --- a/GrampsWebSync/tests/test_sync_flow.py +++ b/GrampsWebSync/tests/test_sync_flow.py @@ -32,7 +32,6 @@ C_UPD_LOC, C_UPD_REM, MODE_BIDIRECTIONAL, - MODE_MERGE, MODE_RESET_TO_LOCAL, MODE_RESET_TO_REMOTE, ) @@ -43,7 +42,14 @@ State, ) -from .scenario import T0, T2, T3, SyncScenario +from .scenario import ( + DEFAULT_URL as URL, + DEFAULT_USERNAME as USERNAME, + T0, + T2, + T3, + SyncScenario, +) class SyncFlowTestCase(unittest.TestCase): @@ -186,13 +192,12 @@ def test_reset_to_remote_makes_the_local_tree_match_the_server(self) -> None: self.assertNotIn("I9100", scenario.local.person_ids()) self.assertNotIn("I9100", scenario.remote.person_ids()) - def test_merge_restores_a_locally_deleted_object(self) -> None: - """Merge mode never deletes; a removal on one side is undone.""" + def test_an_unknown_mode_is_rejected(self) -> None: + """Modes are a closed set; an unknown one must not sync silently.""" scenario = self.make_scenario() scenario.local.delete_person("I0002") - scenario.run(mode=MODE_MERGE) - self.assertIn("I0002", scenario.local.person_ids()) - self.assertIn("I0002", scenario.remote.person_ids()) + result = scenario.run(mode=99) + self.assertIs(result.final_state, State.FAILED) class MediaFileTest(SyncFlowTestCase): @@ -265,7 +270,7 @@ def test_successful_run_records_the_sync_time(self) -> None: scenario.run() - self.assertEqual(scenario.credentials.get_timestamp(), 1_700_000_500.0) + self.assertEqual(scenario.credentials.get_timestamp(URL, USERNAME), 1_700_000_500.0) def test_in_sync_run_also_records_the_sync_time(self) -> None: """Finding no differences still counts as a successful sync.""" @@ -274,14 +279,21 @@ def test_in_sync_run_also_records_the_sync_time(self) -> None: scenario.run() - self.assertEqual(scenario.credentials.get_timestamp(), 1_700_000_900.0) + self.assertEqual( + scenario.credentials.get_timestamp(URL, USERNAME), 1_700_000_900.0 + ) + + def test_each_server_keeps_its_own_baseline(self) -> None: + """Syncing elsewhere must not disturb this server's baseline. - def test_changing_the_url_clears_the_stored_timestamp(self) -> None: - """A timestamp is meaningless against a different tree.""" + The baseline used to be a single value that a changed URL reset, so + alternating between two servers discarded it every time and made every + run after a switch a full "modified in both" comparison. + """ scenario = self.make_scenario() - scenario.credentials.timestamp = T3 + scenario.credentials.timestamps[(URL, USERNAME)] = T3 scenario.run(url="https://elsewhere.example/api") - self.assertNotEqual(scenario.credentials.get_timestamp(), T3) + self.assertEqual(scenario.credentials.get_timestamp(URL, USERNAME), T3) class ProgressTest(SyncFlowTestCase): diff --git a/GrampsWebSync/tests/test_view_mapping.py b/GrampsWebSync/tests/test_view_mapping.py index 9d6ed607b..f8ae7e450 100644 --- a/GrampsWebSync/tests/test_view_mapping.py +++ b/GrampsWebSync/tests/test_view_mapping.py @@ -26,7 +26,8 @@ import unittest import grampswebsync -from grampswebsync import PAGE_FOR_STATE, error_message +from adapters import KeyringUnavailable +from grampswebsync import PAGE_FOR_STATE, error_message, keyring_message from session import ErrorKind, State @@ -67,6 +68,28 @@ def test_detail_is_included_where_it_carries_information(self) -> None: self.assertIn("42", error_message(ErrorKind.SERVER_ERROR, "42")) self.assertIn("boom", error_message(ErrorKind.UNEXPECTED, "boom")) + def test_a_failed_server_task_reports_what_the_server_said(self) -> None: + """This used to render a stringified status dict plus advice to check + the connection, which was neither true nor actionable.""" + message = error_message(ErrorKind.SERVER_TASK_FAILED, "disk full") + self.assertIn("disk full", message) + self.assertNotIn("connection", message.lower()) + + +class KeyringMessageTest(unittest.TestCase): + """An unusable keyring is reported, and under snap it is fixable.""" + + def test_the_snap_command_is_included_when_there_is_one(self) -> None: + problem = KeyringUnavailable( + "denied", snap_command="snap connect gramps:password-manager-service" + ) + self.assertIn("snap connect gramps", keyring_message(problem)) + + def test_elsewhere_the_message_says_what_to_expect_instead(self) -> None: + message = keyring_message(KeyringUnavailable("no backend")) + self.assertNotIn("snap", message.lower()) + self.assertTrue(message.strip()) + if __name__ == "__main__": unittest.main() diff --git a/GrampsWebSync/webapihandler.py b/GrampsWebSync/webapihandler.py index 5f5414730..928f028bb 100644 --- a/GrampsWebSync/webapihandler.py +++ b/GrampsWebSync/webapihandler.py @@ -43,6 +43,41 @@ LOG = logging.getLogger("grampswebsync") +#: Seconds before a request that has produced nothing is abandoned. Without +#: this, ``urlopen`` waits forever and an unreachable-but-listening server +#: hangs the tool with no way out. +TIMEOUT = 60 + + +class ServerTaskFailed(Exception): + """A background task on the server reported failure. + + Carries the server's own description rather than a stringified status dict, + so the message shown to the user says what went wrong. + """ + + +def describe_task_failure(task_status: dict[str, Any]) -> str: + """Extract a readable reason from a failed task status. + + The status dict carries the reason in one of a few shapes depending on how + the task died. Stringifying the whole dict, as this once did, produced a + message no user could act on. + + :param task_status: The server's task status document. + :returns: The most specific description available. + """ + info = task_status.get("info") + if isinstance(info, dict): + for key in ("message", "error", "detail"): + value = info.get(key) + if value: + return str(value) + elif info: + return str(info) + state = task_status.get("state", "FAILURE") + return f"The server reported task state {state}." + def parse_version(version) -> tuple[int, int]: """Simple dependency-free version to parse a SemVer into a list of ints.""" @@ -116,6 +151,10 @@ def __init__( self.fetch_token() self._metadata: dict | None = None + def _open(self, req: Request): + """Open ``req`` with this handler's SSL context and timeout.""" + return urlopen(req, context=self._ctx, timeout=TIMEOUT) + @property def access_token(self) -> str: """Get the access token. Cached after first call unless refresh needed. Auto-refreshing""" @@ -153,7 +192,7 @@ def fetch_metadata(self) -> None: f"{self.url}/metadata/", headers={"Authorization": f"Bearer {self.access_token}", "User-Agent": "GrampsWebSync"}, ) - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: self._metadata = json.load(res) def fetch_token(self) -> None: @@ -166,7 +205,7 @@ def fetch_token(self) -> None: headers={"Content-Type": "application/json", "User-Agent": "GrampsWebSync"}, ) try: - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: res_json = json.load(res) except (UnicodeDecodeError, json.JSONDecodeError, HTTPError): if "/api" not in self.url: @@ -230,7 +269,7 @@ def commit( }, ) json_response: dict | None = None - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: status_code = res.getcode() if status_code == 202: json_response = json.load(res) @@ -264,30 +303,23 @@ def update_task_status( endpoint, headers={"Authorization": f"Bearer {self.access_token}", "User-Agent": "GrampsWebSync"}, ) - try: - with urlopen(req, context=self._ctx) as res: - task_status = json.load(res) - if task_status["state"] == "SUCCESS": - return True - if task_status["state"] in {"FAILURE", "REVOKED"}: - LOG.warning(f"Server task failed: {task_status}") - raise ValueError(str(task_status.get("info", "Server task failed"))) - if progress_callback: - try: - progress = task_status["result_object"]["progress"] - except (KeyError, TypeError): - progress = -1 - progress_callback(progress) - return False - except HTTPError as e: - LOG.warning(f"HTTPError while fetching task status: {e.code} - {e.reason}") - raise ValueError(f"HTTP Error: {e.code} - {e.reason}") - except URLError as e: - LOG.warning(f"URLError while fetching task status: {e.reason}") - raise ValueError(f"URL Error: {e.reason}") - except socket.timeout as e: - LOG.warning(f"Timeout while fetching task status: {e}") - raise ValueError("Connection timed out while fetching task status.") + # HTTPError and URLError are deliberately not wrapped: the caller + # classifies them into specific, actionable messages, which converting + # them to a ValueError would flatten into a generic server error. + with self._open(req) as res: + task_status = json.load(res) + if task_status["state"] == "SUCCESS": + return True + if task_status["state"] in {"FAILURE", "REVOKED"}: + LOG.warning("Server task failed: %s", task_status) + raise ServerTaskFailed(describe_task_failure(task_status)) + if progress_callback: + try: + progress = task_status["result_object"]["progress"] + except (KeyError, TypeError): + progress = -1 + progress_callback(progress) + return False def get_missing_files(self, retry: bool = True) -> list: """Get a list of remote media objects with missing files.""" @@ -296,7 +328,7 @@ def get_missing_files(self, retry: bool = True) -> list: headers={"Authorization": f"Bearer {self.access_token}", "User-Agent": "GrampsWebSync"}, ) try: - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: res_json = json.load(res) except HTTPError as exc: if exc.code == 401 and retry: @@ -319,7 +351,7 @@ def _download_file( headers={"Authorization": f"Bearer {self.access_token}", "User-Agent": "GrampsWebSync"}, ) try: - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: chunk_size = 1024 chunk = res.read(chunk_size) fobj.write(chunk) @@ -367,7 +399,7 @@ def _upload_file(self, url: str, fobj, retry: bool = True): method="PUT", ) try: - with urlopen(req, context=self._ctx) as res: + with self._open(req) as res: pass except HTTPError as exc: if exc.code == 401 and retry: From 70016ea1c8f85a468c940afb1ac96032d5472ece Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 31 Jul 2026 17:01:48 +0200 Subject: [PATCH 075/156] Address copilot comments --- GrampsWebSync/po/template.pot | 193 ++++++++++++++++++--------- GrampsWebSync/session.py | 61 +++++++-- GrampsWebSync/tests/test_recovery.py | 18 +++ GrampsWebSync/webapihandler.py | 2 +- 4 files changed, 200 insertions(+), 74 deletions(-) diff --git a/GrampsWebSync/po/template.pot b/GrampsWebSync/po/template.pot index 61da0f9b4..d56bfc9ec 100644 --- a/GrampsWebSync/po/template.pot +++ b/GrampsWebSync/po/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-27 09:45+0200\n" +"POT-Creation-Date: 2026-07-31 15:54+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,8 +18,8 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:179 -#: GrampsWebSync/grampswebsync.py:237 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -27,115 +27,139 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:123 +#: GrampsWebSync/grampswebsync.py:124 +#, python-format +msgid "" +"The password could not be saved to the system keyring. Snap confinement " +"blocks access until you run: %s" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The password could not be saved to the system keyring. You will need to " +"enter it each time." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:126 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:128 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:130 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:132 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:134 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:137 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:140 +#: GrampsWebSync/grampswebsync.py:162 msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:142 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:144 +#: GrampsWebSync/grampswebsync.py:166 msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:146 +#: GrampsWebSync/grampswebsync.py:168 msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:149 +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:177 #, python-format msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:151 GrampsWebSync/grampswebsync.py:152 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:187 +#: GrampsWebSync/grampswebsync.py:221 msgid "Introduction" msgstr "" -#: GrampsWebSync/grampswebsync.py:195 +#: GrampsWebSync/grampswebsync.py:229 msgid "Login" msgstr "" -#: GrampsWebSync/grampswebsync.py:201 GrampsWebSync/grampswebsync.py:223 +#: GrampsWebSync/grampswebsync.py:235 GrampsWebSync/grampswebsync.py:257 msgid "Progress Information" msgstr "" -#: GrampsWebSync/grampswebsync.py:206 +#: GrampsWebSync/grampswebsync.py:240 msgid "Final confirmation" msgstr "" -#: GrampsWebSync/grampswebsync.py:211 GrampsWebSync/grampswebsync.py:227 +#: GrampsWebSync/grampswebsync.py:245 GrampsWebSync/grampswebsync.py:261 msgid "Summary" msgstr "" -#: GrampsWebSync/grampswebsync.py:216 +#: GrampsWebSync/grampswebsync.py:250 msgid "Media Files" msgstr "" -#: GrampsWebSync/grampswebsync.py:289 +#: GrampsWebSync/grampswebsync.py:341 msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:292 +#: GrampsWebSync/grampswebsync.py:344 msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:296 +#: GrampsWebSync/grampswebsync.py:348 msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:377 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:384 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:385 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:435 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -151,131 +175,180 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:462 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:471 +#: GrampsWebSync/grampswebsync.py:531 msgid "Username: " msgstr "" -#: GrampsWebSync/grampswebsync.py:479 +#: GrampsWebSync/grampswebsync.py:539 msgid "Password: " msgstr "" -#: GrampsWebSync/grampswebsync.py:571 +#: GrampsWebSync/grampswebsync.py:578 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:580 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:588 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:596 -msgid "Merge" +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" msgstr "" -#: GrampsWebSync/grampswebsync.py:622 +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:623 GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:723 GrampsWebSync/grampswebsync.py:728 msgid "Added" msgstr "" -#: GrampsWebSync/grampswebsync.py:624 GrampsWebSync/grampswebsync.py:629 +#: GrampsWebSync/grampswebsync.py:724 GrampsWebSync/grampswebsync.py:729 msgid "Deleted" msgstr "" -#: GrampsWebSync/grampswebsync.py:625 GrampsWebSync/grampswebsync.py:630 -#: GrampsWebSync/grampswebsync.py:632 +#: GrampsWebSync/grampswebsync.py:725 GrampsWebSync/grampswebsync.py:730 +#: GrampsWebSync/grampswebsync.py:732 msgid "Modified" msgstr "" -#: GrampsWebSync/grampswebsync.py:627 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:632 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:689 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:715 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:721 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:723 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:727 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:732 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:761 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:764 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:804 +#: GrampsWebSync/grampswebsync.py:905 #, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" msgstr[0] "" msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:819 +#: GrampsWebSync/grampswebsync.py:920 #, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" msgstr[0] "" msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:857 +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:999 +#, python-format +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:873 +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1033 #, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." msgstr[0] "" msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:883 +#: GrampsWebSync/grampswebsync.py:1043 #, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." msgstr[0] "" msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:896 +#: GrampsWebSync/grampswebsync.py:1056 #, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." msgstr[0] "" msgstr[1] "" -#: GrampsWebSync/grampswebsync.py:905 +#: GrampsWebSync/grampswebsync.py:1065 #, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." diff --git a/GrampsWebSync/session.py b/GrampsWebSync/session.py index bd5357c93..a9a1e2413 100644 --- a/GrampsWebSync/session.py +++ b/GrampsWebSync/session.py @@ -105,6 +105,7 @@ class Step(Enum): DIFF = auto() # import it and compare (database) APPLY_LOCAL = auto() # write the local half (database) PUSH_REMOTE = auto() # send the remote half (network) + SCAN_MEDIA = auto() # ask which files the server lacks (network) TRANSFER = auto() # move media files (network) @@ -308,6 +309,7 @@ def next_state(state: State, session: SyncSession) -> State: Step.DIFF: State.COMPARING, Step.APPLY_LOCAL: State.APPLYING, Step.PUSH_REMOTE: State.APPLYING, + Step.SCAN_MEDIA: State.APPLYING, # overridden by _scan_resume_state Step.TRANSFER: State.TRANSFERRING, } @@ -387,6 +389,8 @@ def __init__( #: after a failed push must not re-run the local commit. Everything #: else a step hands to its successor travels as an argument. self._payload: list[dict[str, Any]] | None = None + #: Where a media scan was scheduled from, so a retry resumes there. + self._scan_resume_state: State = State.COMPARING self._closing = False # -------------------------------------------------------- @@ -491,7 +495,8 @@ def _run(self, step: Step, func, on_success) -> None: """ runner = ( self.io_runner - if step in (Step.FETCH, Step.PUSH_REMOTE, Step.TRANSFER) + if step + in (Step.FETCH, Step.PUSH_REMOTE, Step.SCAN_MEDIA, Step.TRANSFER) else self.runner ) runner.run(func, on_success, lambda exc: self._on_step_error(exc, step)) @@ -580,7 +585,11 @@ def retry(self) -> None: LOG.info("Retrying sync from %s.", step.name) self.error = None self.failed_in = None - self._goto(STATE_FOR_STEP[step]) + self._goto( + self._scan_resume_state + if step is Step.SCAN_MEDIA + else STATE_FOR_STEP[step] + ) if step is Step.FETCH: self._run(Step.FETCH, self._fetch_xml, self._on_fetched) elif step is Step.DIFF: @@ -589,6 +598,8 @@ def retry(self) -> None: self._run(Step.APPLY_LOCAL, self._apply_local, self._on_local_applied) elif step is Step.PUSH_REMOTE: self._run(Step.PUSH_REMOTE, self._push_remote, self._on_applied) + elif step is Step.SCAN_MEDIA: + self._start_media_scan() elif step is Step.TRANSFER: self._start_transfer() @@ -667,11 +678,12 @@ def _on_compared(self, _result: Any) -> None: """Move on once the diff is available.""" if self._closing: return - if not self.changes: - LOG.info("Databases are in sync.") - self.credentials.set_timestamp(self.url, self.username, self.clock.now()) - self._scan_media() - self._advance() + if self.changes: + self._advance() + return + LOG.info("Databases are in sync.") + self.credentials.set_timestamp(self.url, self.username, self.clock.now()) + self._start_media_scan() # -------------------------------------------------------- # Applying @@ -762,28 +774,51 @@ def _on_applied(self, _result: Any) -> None: if self._closing: return self.credentials.set_timestamp(self.url, self.username, self.clock.now()) - self._scan_media() - self._advance() + self._start_media_scan() # -------------------------------------------------------- # Media # -------------------------------------------------------- - def _scan_media(self) -> None: + def _start_media_scan(self) -> None: + """Ask the server which files it lacks, off the main loop. + + The state to come back to is captured here, because a failure moves the + session to ``FAILED`` and a retry has to resume from where the scan was + scheduled rather than from wherever it ended up. + """ + self._scan_resume_state = self.state + self._run(Step.SCAN_MEDIA, self._fetch_remote_missing, self._on_media_scanned) + + def _fetch_remote_missing(self) -> list[dict[str, Any]]: + """Return the server's list of media objects with no file. Network only.""" + if self._closing: + return [] + assert self.backend is not None + return self.backend.get_missing_files() or [] + + def _on_media_scanned(self, remote: list[dict[str, Any]] | None) -> None: + """Combine the server's answer with a local scan, back on the main loop.""" + if self._closing: + return + self._scan_media(remote or []) + self._advance() + + def _scan_media(self, remote: list[dict[str, Any]]) -> None: """Work out which media files are missing, and on which side. A file absent from both sides cannot be transferred in either direction, so it is separated out here rather than being attempted twice and reported as two failures. + + :param remote: The server's missing-file list, already fetched. """ - assert self.backend is not None local_missing = { media.handle: media.gramps_id for media in self.db1.iter_media() if not self.media.exists(media) } remote_missing = { - media["handle"]: media["gramps_id"] - for media in self.backend.get_missing_files() or [] + media["handle"]: media["gramps_id"] for media in remote } both = set(local_missing) & set(remote_missing) diff --git a/GrampsWebSync/tests/test_recovery.py b/GrampsWebSync/tests/test_recovery.py index 523b43fa6..8ab171427 100644 --- a/GrampsWebSync/tests/test_recovery.py +++ b/GrampsWebSync/tests/test_recovery.py @@ -149,6 +149,24 @@ def test_retry_without_a_recorded_step_does_nothing(self) -> None: self.assertIsNone(session.error) +class MediaScanTest(RecoveryTestCase): + """Asking the server which files it lacks is a network step of its own.""" + + def test_a_failed_scan_is_retryable_as_its_own_step(self) -> None: + """It used to run inline on the main loop, freezing the UI while it ran.""" + scenario = self.make_scenario() + scenario.server.fail_next("get_missing_files", http_error(500)) + session = self.connect(scenario) + + self.assertIs(session.state, State.FAILED) + self.assertIs(session.failed_in, Step.SCAN_MEDIA) + + session.retry() + + self.assertIs(session.state, State.DONE) + self.assertIsNone(session.error) + + class StaleComparisonTest(RecoveryTestCase): """Edits made while the review page is open must not be overwritten. diff --git a/GrampsWebSync/webapihandler.py b/GrampsWebSync/webapihandler.py index 928f028bb..489f1826b 100644 --- a/GrampsWebSync/webapihandler.py +++ b/GrampsWebSync/webapihandler.py @@ -352,7 +352,7 @@ def _download_file( ) try: with self._open(req) as res: - chunk_size = 1024 + chunk_size = 64 * 1024 chunk = res.read(chunk_size) fobj.write(chunk) while chunk: From e72bd4c7ddc1674f8d593e0dce842d49df48f3fc Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 31 Jul 2026 17:17:49 +0200 Subject: [PATCH 076/156] Address second round of comments --- GrampsWebSync/adapters.py | 18 +++++++++++++---- GrampsWebSync/session.py | 5 ++++- GrampsWebSync/tests/test_credentials.py | 26 ++++++++++++++++++++++--- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/GrampsWebSync/adapters.py b/GrampsWebSync/adapters.py index 97cc971ef..46bb41b1e 100644 --- a/GrampsWebSync/adapters.py +++ b/GrampsWebSync/adapters.py @@ -160,15 +160,25 @@ def set(self, service: str, username: str, password: str) -> bool: return False return True - def delete(self, service: str, username: str) -> None: - """Remove a stored password, ignoring one that was never there.""" + def delete(self, service: str, username: str) -> bool: + """Remove a stored password. + + :returns: Whether the password is now gone. + """ keyring = self._module() if keyring is None: - return + return False try: keyring.delete_password(service, username) except Exception as exc: # noqa: BLE001 -- absent entries raise too - LOG.debug("Keyring delete for %s failed: %s", username, exc) + # Deleting what was never there raises, and is harmless; a keyring + # that is actually broken still has the password afterwards. + if self.get(service, username) is None: + LOG.debug("Nothing to delete for %s: %s", username, exc) + return True + self._failed("delete", exc) + return False + return True # ------------------------------------------------------------ diff --git a/GrampsWebSync/session.py b/GrampsWebSync/session.py index a9a1e2413..998ae39b4 100644 --- a/GrampsWebSync/session.py +++ b/GrampsWebSync/session.py @@ -481,6 +481,9 @@ def _classify(self, exc: BaseException, *, login: bool = False) -> SyncError: return classify_http_error(exc, login=login) if isinstance(exc, URLError): return SyncError(ErrorKind.CONNECTION_FAILED, str(exc.reason)) + # A read that times out raises this directly, not wrapped in URLError. + if isinstance(exc, TimeoutError): + return SyncError(ErrorKind.CONNECTION_FAILED, str(exc)) if isinstance(exc, ValueError): kind = ErrorKind.INVALID_RESPONSE if login else ErrorKind.SERVER_ERROR return SyncError(kind, str(exc)) @@ -822,7 +825,7 @@ def _scan_media(self, remote: list[dict[str, Any]]) -> None: } both = set(local_missing) & set(remote_missing) - self.missing_both = [(local_missing[h], h) for h in both] + self.missing_both = sorted((local_missing[h], h) for h in both) self.missing_local = [ (gid, h) for h, gid in local_missing.items() if h not in both ] diff --git a/GrampsWebSync/tests/test_credentials.py b/GrampsWebSync/tests/test_credentials.py index 8a6acbcdd..e0e19d6b3 100644 --- a/GrampsWebSync/tests/test_credentials.py +++ b/GrampsWebSync/tests/test_credentials.py @@ -354,11 +354,31 @@ def test_a_failure_stops_further_attempts(self) -> None: self.assertEqual(backend.calls, ["set"]) def test_deleting_a_missing_entry_is_not_a_failure(self) -> None: - """Backends raise when asked to delete something that is not there.""" - keyring = KeyringOverBackend(ExplodingBackend(RuntimeError("no such item"))) - keyring.delete("svc", "nobody") + """Backends raise when asked to delete something that is not there. + + A working keyring still reads cleanly, which is how that is told apart + from a keyring that cannot delete because it is broken. + """ + + class AbsentEntryBackend(ExplodingBackend): + def get_password(self, *_args): + return None + + keyring = KeyringOverBackend(AbsentEntryBackend(RuntimeError("no such item"))) + self.assertTrue(keyring.delete("svc", "nobody")) self.assertIsNone(keyring.unavailable) + def test_a_delete_that_leaves_the_password_behind_is_a_failure(self) -> None: + """Otherwise turning off "remember password" silently does nothing.""" + + class StubbornBackend(ExplodingBackend): + def get_password(self, *_args): + return "still here" + + keyring = KeyringOverBackend(StubbornBackend(RuntimeError("denied"))) + self.assertFalse(keyring.delete("svc", "user")) + self.assertIsNotNone(keyring.unavailable) + class SnapHintTest(unittest.TestCase): """Under snap the failure is a setting the user can change.""" From d92593f6a438475fe92ddf8ef240342ce185f874 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 31 Jul 2026 17:36:17 +0200 Subject: [PATCH 077/156] Address 3rd round of comments --- GrampsWebSync/adapters.py | 6 ++++-- GrampsWebSync/grampswebsync.py | 11 ++++------- GrampsWebSync/po/template.pot | 10 +++++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/GrampsWebSync/adapters.py b/GrampsWebSync/adapters.py index 46bb41b1e..e469008c0 100644 --- a/GrampsWebSync/adapters.py +++ b/GrampsWebSync/adapters.py @@ -172,8 +172,10 @@ def delete(self, service: str, username: str) -> bool: keyring.delete_password(service, username) except Exception as exc: # noqa: BLE001 -- absent entries raise too # Deleting what was never there raises, and is harmless; a keyring - # that is actually broken still has the password afterwards. - if self.get(service, username) is None: + # that is actually broken still has the password afterwards. A read + # that fails proves nothing either way, so it counts as a failure. + before = self.unavailable + if self.get(service, username) is None and self.unavailable is before: LOG.debug("Nothing to delete for %s: %s", username, exc) return True self._failed("delete", exc) diff --git a/GrampsWebSync/grampswebsync.py b/GrampsWebSync/grampswebsync.py index 4bb25b4b2..42f5f407f 100644 --- a/GrampsWebSync/grampswebsync.py +++ b/GrampsWebSync/grampswebsync.py @@ -113,20 +113,17 @@ def keyring_message(problem: KeyringUnavailable) -> str: """Return the localized notice for an unusable keyring. - Under snap the failure is a confinement setting the user can change, so the - message carries the command rather than only apologizing. - :param problem: What the keyring reported. :returns: A message suitable for display. """ if problem.snap_command: return _( - "The password could not be saved to the system keyring. " - "Snap confinement blocks access until you run: %s" + "The system keyring could not be used. Snap confinement blocks " + "access until you run: %s" ) % problem.snap_command return _( - "The password could not be saved to the system keyring. " - "You will need to enter it each time." + "The system keyring could not be used. " + "You will need to enter your password each time." ) diff --git a/GrampsWebSync/po/template.pot b/GrampsWebSync/po/template.pot index d56bfc9ec..9dc578895 100644 --- a/GrampsWebSync/po/template.pot +++ b/GrampsWebSync/po/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-31 15:54+0200\n" +"POT-Creation-Date: 2026-07-31 17:35+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -30,14 +30,14 @@ msgstr "" #: GrampsWebSync/grampswebsync.py:124 #, python-format msgid "" -"The password could not be saved to the system keyring. Snap confinement " -"blocks access until you run: %s" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" #: GrampsWebSync/grampswebsync.py:128 msgid "" -"The password could not be saved to the system keyring. You will need to " -"enter it each time." +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" #: GrampsWebSync/grampswebsync.py:145 From 174eb31c6076ce2cfd0fe149593262b197f924c1 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 1 Aug 2026 09:17:20 -0700 Subject: [PATCH 078/156] Merge Gramps Web Sync refactoring No. 2- #1004 #1004 --- GrampsWebSync/grampswebsync.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GrampsWebSync/grampswebsync.gpr.py b/GrampsWebSync/grampswebsync.gpr.py index c8f2399b1..eed02b829 100644 --- a/GrampsWebSync/grampswebsync.gpr.py +++ b/GrampsWebSync/grampswebsync.gpr.py @@ -28,7 +28,7 @@ id="gramps_web_sync", name=_("Gramps Web Sync"), description=_("Synchronizes a local database with a Gramps Web instance."), - version = '1.3.14', + version = '1.4.1', gramps_target_version="6.1", status=STABLE, fname="grampswebsync.py", From 11cd4724529199031bc982acf9718bc96d4352dc Mon Sep 17 00:00:00 2001 From: prculley Date: Fri, 31 Jul 2026 17:00:00 -0500 Subject: [PATCH 079/156] Update Prerequsites checker to report on both python-bsddb3 and berkeleydb adapters for bsddb --- .../PrerequisitesCheckerGramplet.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py b/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py index 8ee9140a7..9e23b3589 100755 --- a/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py +++ b/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py @@ -778,24 +778,34 @@ def check6_bsddb3(self): # Start check try: - import bsddb3 as bsddb - - bsddb_str = bsddb.__version__ # Python adaptation layer - # Underlying DB library - bsddb_db_str = ( - str(bsddb.db.version()) - .replace(", ", ".") - .replace("(", "") - .replace(")", "") - ) + import berkeleydb as bsddb + bsdadapt = "BerkeleyDB" except ImportError: - bsddb_str = _("not found") - bsddb_db_str = _("not found") + try: + import bsddb3 as bsddb + bsdadapt = "Python-bsddb3" + except ImportError: + result = ( + _(" • Berkeley Database library (bsddb3: ") + + _("not found") + + _(")\n\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley database") + ) + self.append_text(result) + return + + bsddb_str = bsddb.__version__ # Python adaptation layer + # Underlying DB library + bsddb_db_str = ( + str(bsddb.db.version()) + .replace(", ", ".") + .replace("(", "") + .replace(")", "") + ) result = ( _(" • Berkeley Database library (bsddb3: ") + bsddb_db_str - + ") (Python-bsddb3 : " + + ") (" + bsdadapt + " adapter : " + bsddb_str + ")" ) From c799d1e30d599a47858f2e325e14f618d5b87d11 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 1 Aug 2026 09:28:28 -0700 Subject: [PATCH 080/156] Merge Update Prerequsites checker#1006 --- .../PrerequisitesCheckerGramplet.gpr.py | 2 +- PrerequisitesCheckerGramplet/po/template.pot | 243 +++++++++--------- 2 files changed, 126 insertions(+), 119 deletions(-) diff --git a/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py b/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py index 177c20350..784e09a11 100644 --- a/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py +++ b/PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py @@ -28,7 +28,7 @@ id="Prerequisites Checker Gramplet", name=_("Prerequisites Checker"), description=_("Prerequisites Checker Gramplet"), - version = '1.2.14', + version = '1.2.15', gramps_target_version="6.1", status=STABLE, fname="PrerequisitesCheckerGramplet.py", diff --git a/PrerequisitesCheckerGramplet/po/template.pot b/PrerequisitesCheckerGramplet/po/template.pot index 12268657c..c5506a551 100755 --- a/PrerequisitesCheckerGramplet/po/template.pot +++ b/PrerequisitesCheckerGramplet/po/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-06-02 12:49-0700\n" +"POT-Creation-Date: 2026-08-01 09:23-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -182,36 +182,35 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:653 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:720 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:752 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:792 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:793 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:823 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:824 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:896 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:899 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1078 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1091 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:790 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:833 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:834 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:906 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:909 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1088 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1101 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1103 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1117 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1189 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1238 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1297 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1300 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1312 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1339 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1396 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1398 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1476 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1562 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1741 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1788 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1789 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2202 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2204 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2265 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2272 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2302 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2304 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1111 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1113 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1127 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1248 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1307 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1310 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1322 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1349 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1406 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1408 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1486 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1572 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1751 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1799 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2212 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2214 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2275 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2282 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2312 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2314 msgid "not found" msgstr "" @@ -241,9 +240,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -266,9 +265,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -277,10 +276,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -300,193 +299,201 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1178 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1186 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1188 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1196 msgid "installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1236 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1246 msgid "Installed but does not supply version" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1708 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1737 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1710 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1718 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1747 msgid "Installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -524,19 +531,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" From 0979dc36280729823e5680d6320fa36463a76442 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 1 Aug 2026 09:41:00 -0700 Subject: [PATCH 081/156] Merge new strings from GrampsWebSync and PrerequisitesChecker --- po/addons.pot | 434 +++++++++++++++++++++++++++++--------------------- 1 file changed, 249 insertions(+), 185 deletions(-) diff --git a/po/addons.pot b/po/addons.pot index 7fdf571b1..a0ea7b9cb 100644 --- a/po/addons.pot +++ b/po/addons.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15426,8 +15426,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15435,140 +15435,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15584,80 +15559,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16407,11 +16468,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20507,9 +20563,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20532,9 +20588,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20543,10 +20599,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20566,178 +20622,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20775,19 +20839,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" From af5483670ba40cac5e5ab66ceda449efdf585458 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Fri, 31 Jul 2026 09:06:00 -0700 Subject: [PATCH 082/156] PostgreSQL: translate LIKE to ILIKE for SQLite-compatible case-insensitive matching SQLite's LIKE is case-insensitive by default (ASCII), but PostgreSQL's LIKE is case-sensitive; ILIKE is PostgreSQL's case-insensitive equivalent. Add this to Connection.execute()'s existing set of dialect rewrites (REGEXP, LIMIT, BLOB) so any SQLite-flavored LIKE query behaves the same against PostgreSQL. No current caller emits LIKE yet; this is forward-looking so a future caller doesn't need to special-case the backend. Co-Authored-By: Claude Sonnet 5 --- PostgreSQL/postgresql.py | 6 ++++++ PostgreSQL/tests/test_sql_translations.py | 26 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/PostgreSQL/postgresql.py b/PostgreSQL/postgresql.py index c7f91b18b..a4382a3c9 100644 --- a/PostgreSQL/postgresql.py +++ b/PostgreSQL/postgresql.py @@ -209,6 +209,12 @@ def check_collation(self, locale): def execute(self, *args, **kwargs): sql = args[0].replace("?", "%s") # qmark → format paramstyle sql = sql.replace(" REGEXP ", " ~ ") # SQLite REGEXP → PostgreSQL ~ + # SQLite LIKE is case-insensitive (ASCII); PostgreSQL LIKE is + # case-sensitive, so use ILIKE for equivalent behavior. Note that + # PostgreSQL's ILIKE case-folding follows the connection's locale/ + # collation, while SQLite's is ASCII-only, so non-ASCII patterns may + # fold slightly differently between the two backends. + sql = re.sub(r"\bLIKE\b", "ILIKE", sql, flags=re.IGNORECASE) # TODO: remove when gramps PR #2178 (_quote_column) is merged into core sql = sql.replace("ON media(desc)", "ON media(desc_)") sql = re.sub(r'\bBLOB\b', 'BYTEA', sql) # SQLite BLOB → PostgreSQL BYTEA diff --git a/PostgreSQL/tests/test_sql_translations.py b/PostgreSQL/tests/test_sql_translations.py index 3b4f3976b..7e33726c8 100644 --- a/PostgreSQL/tests/test_sql_translations.py +++ b/PostgreSQL/tests/test_sql_translations.py @@ -24,6 +24,7 @@ These tests cover every rewrite rule applied before a query reaches psycopg2: - qmark → format paramstyle (? → %s) - REGEXP operator (REGEXP → ~) + - LIKE operator (LIKE → ILIKE) - two-arg LIMIT (LIMIT offset, count → LIMIT count OFFSET offset) - unlimited LIMIT (LIMIT -1 → LIMIT ALL) @@ -144,6 +145,31 @@ def test_no_regexp_unchanged(self): self.assertEqual(_translated(sql), sql) +# ------------------------------------------------------------------------- +# +# TestExecuteLikeOperator +# +# ------------------------------------------------------------------------- +class TestExecuteLikeOperator(unittest.TestCase): + """LIKE → ILIKE substitution.""" + + def test_like_replaced(self): + result = _translated("SELECT * FROM person WHERE surname LIKE ?") + self.assertIn("ILIKE", result) + + def test_lowercase_like_replaced(self): + result = _translated("SELECT * FROM person WHERE surname like ?") + self.assertIn("ILIKE", result) + + def test_ilike_not_double_rewritten(self): + sql = "SELECT * FROM person WHERE surname ILIKE %s" + self.assertEqual(_translated(sql), sql) + + def test_no_like_unchanged(self): + sql = "SELECT * FROM person WHERE surname = %s" + self.assertEqual(_translated(sql), sql) + + # ------------------------------------------------------------------------- # # TestExecuteLimitTranslations From a33bd0ce7fee3c30d291b2284436c258b8a51073 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Sat, 1 Aug 2026 10:08:33 -0700 Subject: [PATCH 083/156] Merge PostgreSQL: translate LIKE to ILIKE for dialect-consistent case-insensitive matching- #1005 #1005 --- PostgreSQL/postgresql.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PostgreSQL/postgresql.gpr.py b/PostgreSQL/postgresql.gpr.py index a5e6fd21d..9cb5a1244 100644 --- a/PostgreSQL/postgresql.gpr.py +++ b/PostgreSQL/postgresql.gpr.py @@ -23,7 +23,7 @@ name=_("PostgreSQL"), name_accell=_("_PostgreSQL Database"), description=_("PostgreSQL Database"), - version = '1.0.23', + version = '1.0.24', gramps_target_version="6.1", status=STABLE, audience=EXPERT, From 538311f3383f306f32d557d1be81b79ff343674d Mon Sep 17 00:00:00 2001 From: David Straub Date: Mon, 3 Aug 2026 09:03:14 +0200 Subject: [PATCH 084/156] SharedPostgreSQL: prevent race on init; case insensitive like --- SharedPostgreSQL/sharedpostgresql.py | 42 ++- SharedPostgreSQL/tests/test_initialize.py | 294 ++++++++++++++++++ .../tests/test_sql_translations.py | 35 +++ 3 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 SharedPostgreSQL/tests/test_initialize.py diff --git a/SharedPostgreSQL/sharedpostgresql.py b/SharedPostgreSQL/sharedpostgresql.py index 98feec0b3..95a0a96ba 100644 --- a/SharedPostgreSQL/sharedpostgresql.py +++ b/SharedPostgreSQL/sharedpostgresql.py @@ -24,6 +24,7 @@ Backend for PostgreSQL database. """ +import hashlib import os import re from uuid import uuid4 @@ -94,11 +95,7 @@ def _initialize(self, directory, username, password): config_mgr.register("tree.uuid", "") if not os.path.exists(config_file): - config_mgr.set("database.dbname", "gramps") - config_mgr.set("database.host", config.get("database.host")) - config_mgr.set("database.port", config.get("database.port")) - config_mgr.set("tree.uuid", uuid4().hex) - config_mgr.save() + self._create_settings(config_file, config_mgr, directory, username, password) config_mgr.load() @@ -121,6 +118,37 @@ def _initialize(self, directory, username, password): except psycopg2.OperationalError as msg: raise DbConnectionError(str(msg), config_file) + def _create_settings(self, config_file, config_mgr, directory, username, password): + """Create settings.ini for a tree that has not been initialized yet.""" + host = config.get("database.host") + port = config.get("database.port") + dbkwargs = {"dbname": "gramps", "host": host, "port": port} + if username: + dbkwargs["user"] = username + if password: + dbkwargs["password"] = password + # Two processes opening a brand-new tree at the same time would each + # generate a UUID and overwrite each other's settings.ini. Serialize + # on the database, since the filesystem may not do so itself. + digest = hashlib.sha256(os.path.abspath(directory).encode()).digest() + lock_key = int.from_bytes(digest[:8], "big", signed=True) + try: + conn = psycopg2.connect(**dbkwargs) + except psycopg2.OperationalError as msg: + raise DbConnectionError(str(msg), config_file) + try: + conn.autocommit = True + # The lock is released when the connection closes. + conn.cursor().execute("SELECT pg_advisory_lock(%s)", [lock_key]) + if not os.path.exists(config_file): + config_mgr.set("database.dbname", "gramps") + config_mgr.set("database.host", host) + config_mgr.set("database.port", port) + config_mgr.set("tree.uuid", uuid4().hex) + config_mgr.save() + finally: + conn.close() + # ------------------------------------------------------------------------- # @@ -325,6 +353,10 @@ def _translate_sql(query): """ sql = query.replace("?", "%s") # qmark -> format paramstyle sql = sql.replace(" REGEXP ", " ~ ") # SQLite REGEXP -> PostgreSQL ~ + # SQLite LIKE is case-insensitive (ASCII), PostgreSQL's is not; ILIKE is + # the case-insensitive equivalent. Its folding follows the connection + # locale, so non-ASCII patterns may fold differently than under SQLite. + sql = re.sub(r"\bLIKE\b", "ILIKE", sql, flags=re.IGNORECASE) sql = sql.replace("INTEGER PRIMARY KEY", "SERIAL PRIMARY KEY") sql = re.sub(r"\bBLOB\b", "BYTEA", sql) # SQLite BLOB -> PostgreSQL BYTEA # LIMIT offset, count -> LIMIT count OFFSET offset; a count of -1 means all diff --git a/SharedPostgreSQL/tests/test_initialize.py b/SharedPostgreSQL/tests/test_initialize.py new file mode 100644 index 000000000..083cb136c --- /dev/null +++ b/SharedPostgreSQL/tests/test_initialize.py @@ -0,0 +1,294 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 David Straub +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Unit tests for SharedPostgreSQL._create_settings(). + +Several processes can open a never-before-opened tree at the same time, and +each would generate its own tree UUID. Initialization is therefore serialized +on a PostgreSQL advisory lock rather than on the filesystem, which may not +order concurrent writes to settings.ini. These tests cover: + + - the advisory lock key derived from the tree directory + - the losing process adopting the winner's settings.ini + - the lock being taken before settings.ini is inspected + - the connection being closed (releasing the lock) even on failure + +The concurrency itself is not exercised here; that needs a real server and +several processes. psycopg2 is stubbed so no database is required. + +Run with:: + + python3 -m unittest SharedPostgreSQL.tests.test_initialize -v +""" + +# ------------------------------------------------------------------------- +# +# Standard python modules +# +# ------------------------------------------------------------------------- +import os +import shutil +import sys +import tempfile +import unittest +from unittest import mock + +# ------------------------------------------------------------------------- +# +# Stub psycopg2 before the addon is imported so no real DB driver is needed +# +# ------------------------------------------------------------------------- +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +_mock_psycopg2 = mock.MagicMock() +_mock_psycopg2.paramstyle = "format" +_mock_psycopg2.OperationalError = Exception +sys.modules.setdefault("psycopg2", _mock_psycopg2) + +# ------------------------------------------------------------------------- +# +# Gramps modules (required by the addon's import chain) +# +# ------------------------------------------------------------------------- +try: + import gramps +except ImportError as _err: + raise unittest.SkipTest("gramps package not available: %s" % _err) + +if "GRAMPS_RESOURCES" not in os.environ: + os.environ["GRAMPS_RESOURCES"] = os.path.dirname(os.path.dirname(gramps.__file__)) + +try: + from gramps.gen.utils.configmanager import ConfigManager + + from SharedPostgreSQL import sharedpostgresql + from SharedPostgreSQL.sharedpostgresql import SharedPostgreSQL +except Exception as _err: + raise unittest.SkipTest("SharedPostgreSQL module unavailable: %s" % _err) + + +# ------------------------------------------------------------------------- +# +# Base class +# +# ------------------------------------------------------------------------- +class CreateSettingsTestCase(unittest.TestCase): + """Shared fixture: an empty tree directory and a stubbed connection.""" + + def setUp(self): + self.pg = SharedPostgreSQL.__new__(SharedPostgreSQL) + self.tmpdir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, self.tmpdir, ignore_errors=True) + self.config_file = os.path.join(self.tmpdir, "settings.ini") + + def make_config_mgr(self, config_file=None): + """A ConfigManager registered the way _initialize() registers it.""" + config_mgr = ConfigManager(config_file or self.config_file) + config_mgr.register("database.dbname", "") + config_mgr.register("database.host", "") + config_mgr.register("database.port", "") + config_mgr.register("tree.uuid", "") + return config_mgr + + def create_settings(self, directory=None, config_mgr=None, conn=None): + """Run _create_settings() against a stubbed psycopg2 connection.""" + directory = directory or self.tmpdir + config_file = os.path.join(directory, "settings.ini") + if config_mgr is None: + config_mgr = self.make_config_mgr(config_file) + conn = conn if conn is not None else mock.MagicMock() + with mock.patch.object( + sharedpostgresql.psycopg2, "connect", return_value=conn + ) as connect: + self.pg._create_settings(config_file, config_mgr, directory, None, None) + return conn, connect + + @staticmethod + def lock_key(conn): + """The advisory lock key passed to pg_advisory_lock().""" + sql, params = conn.cursor.return_value.execute.call_args[0] + assert "pg_advisory_lock" in sql, sql + return params[0] + + def stored_uuid(self, config_file=None): + """Read tree.uuid back from the settings file on disk.""" + config_mgr = self.make_config_mgr(config_file) + config_mgr.load() + return config_mgr.get("tree.uuid") + + +# ------------------------------------------------------------------------- +# +# TestAdvisoryLockKey +# +# ------------------------------------------------------------------------- +class TestAdvisoryLockKey(CreateSettingsTestCase): + """The lock key is derived deterministically from the tree directory.""" + + def test_same_directory_gives_same_key(self): + """Racing processes must agree on the key or the lock cannot bind.""" + first, _ = self.create_settings() + second, _ = self.create_settings() + self.assertEqual(self.lock_key(first), self.lock_key(second)) + + def test_trailing_separator_gives_same_key(self): + """abspath() normalizes the path, so a trailing slash is harmless.""" + plain, _ = self.create_settings(directory=self.tmpdir) + slashed, _ = self.create_settings(directory=self.tmpdir + os.sep) + self.assertEqual(self.lock_key(plain), self.lock_key(slashed)) + + def test_different_directories_give_different_keys(self): + """Unrelated trees must not serialize against each other.""" + other = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, other, ignore_errors=True) + mine, _ = self.create_settings() + theirs, _ = self.create_settings(directory=other) + self.assertNotEqual(self.lock_key(mine), self.lock_key(theirs)) + + def test_key_fits_signed_64_bit(self): + """pg_advisory_lock takes a bigint; a wider value is a runtime error.""" + conn, _ = self.create_settings() + self.assertGreaterEqual(self.lock_key(conn), -(2**63)) + self.assertLess(self.lock_key(conn), 2**63) + + def test_high_bit_digest_yields_negative_key(self): + """A digest with the top bit set must wrap to a negative bigint. + + Reading the digest unsigned would produce a value above 2**63 that + PostgreSQL rejects, and only for the fraction of paths whose hash + happens to have that bit set -- so pin it down explicitly. + """ + digest = mock.MagicMock() + digest.digest.return_value = b"\xff" * 32 + with mock.patch.object( + sharedpostgresql.hashlib, "sha256", return_value=digest + ): + conn, _ = self.create_settings() + self.assertEqual(self.lock_key(conn), -1) + + +# ------------------------------------------------------------------------- +# +# TestCreateSettingsRace +# +# ------------------------------------------------------------------------- +class TestCreateSettingsRace(CreateSettingsTestCase): + """Only the process holding the lock may generate the tree UUID.""" + + def test_winner_writes_uuid(self): + """Baseline: an uncontended call does create the settings file.""" + self.create_settings() + self.assertTrue(os.path.exists(self.config_file)) + self.assertTrue(self.stored_uuid()) + + def test_loser_adopts_winners_uuid(self): + """A settings file appearing while we block on the lock is kept. + + This is the failure that wedged trees before the lock existed: both + processes generated a UUID and the second overwrote the first, so the + data written under the first UUID became unreachable. + """ + conn = mock.MagicMock() + conn.cursor.return_value.execute.side_effect = self._win_race + self.create_settings(conn=conn) + self.assertEqual(self.stored_uuid(), "winner") + + def test_loser_leaves_file_byte_identical(self): + """The loser must not rewrite the file at all, not even equivalently.""" + conn = mock.MagicMock() + conn.cursor.return_value.execute.side_effect = self._win_race + self.create_settings(conn=conn) + with open(self.config_file, encoding="utf-8") as fh: + self.assertEqual(fh.read(), self._WINNER_INI) + + _WINNER_INI = "[database]\ndbname='gramps'\n\n[tree]\nuuid='winner'\n\n" + + def _win_race(self, *args, **kwargs): + """Simulate another process winning while this one waits for the lock.""" + with open(self.config_file, "w", encoding="utf-8") as fh: + fh.write(self._WINNER_INI) + + +# ------------------------------------------------------------------------- +# +# TestCreateSettingsLockOrdering +# +# ------------------------------------------------------------------------- +class TestCreateSettingsLockOrdering(CreateSettingsTestCase): + """The lock must be held before settings.ini is inspected.""" + + def test_lock_precedes_existence_check(self): + """Checking first and locking second would reopen the race window.""" + events = [] + real_exists = os.path.exists + + def recording_exists(path): + if path == self.config_file: + events.append("exists") + return real_exists(path) + + conn = mock.MagicMock() + conn.cursor.return_value.execute.side_effect = lambda *a, **k: events.append( + "lock" + ) + with mock.patch("os.path.exists", recording_exists): + self.create_settings(conn=conn) + + self.assertEqual(events, ["lock", "exists"]) + + +# ------------------------------------------------------------------------- +# +# TestCreateSettingsLockRelease +# +# ------------------------------------------------------------------------- +class TestCreateSettingsLockRelease(CreateSettingsTestCase): + """The lock is released by closing the session, so close() must always run. + + There is no explicit pg_advisory_unlock; a session level lock is dropped + when the connection ends. A leaked connection would therefore block every + other process opening the same tree. + """ + + def test_connection_closed_on_success(self): + conn, _ = self.create_settings() + conn.close.assert_called_once_with() + + def test_connection_closed_when_save_fails(self): + config_mgr = mock.MagicMock() + config_mgr.save.side_effect = RuntimeError("read-only filesystem") + conn = mock.MagicMock() + with self.assertRaises(RuntimeError): + self.create_settings(config_mgr=config_mgr, conn=conn) + conn.close.assert_called_once_with() + + def test_connection_closed_when_lock_fails(self): + conn = mock.MagicMock() + conn.cursor.return_value.execute.side_effect = RuntimeError("lock timeout") + with self.assertRaises(RuntimeError): + self.create_settings(conn=conn) + conn.close.assert_called_once_with() + + +if __name__ == "__main__": + unittest.main() diff --git a/SharedPostgreSQL/tests/test_sql_translations.py b/SharedPostgreSQL/tests/test_sql_translations.py index 08998fd1c..4f6444fa2 100644 --- a/SharedPostgreSQL/tests/test_sql_translations.py +++ b/SharedPostgreSQL/tests/test_sql_translations.py @@ -25,6 +25,7 @@ These tests cover every rewrite rule applied before a query reaches psycopg2: - qmark -> format paramstyle (? -> %s) - REGEXP operator (REGEXP -> ~) + - LIKE operator (LIKE -> ILIKE) - autoincrement primary key (INTEGER PRIMARY KEY -> SERIAL PRIMARY KEY) - BLOB column type (BLOB -> BYTEA) - two-arg LIMIT (LIMIT offset, count -> LIMIT count OFFSET offset) @@ -152,6 +153,40 @@ def test_no_regexp_unchanged(self): self.assertEqual(_translated(sql), sql) +# ------------------------------------------------------------------------- +# +# TestExecuteLikeOperator +# +# ------------------------------------------------------------------------- +class TestExecuteLikeOperator(unittest.TestCase): + """LIKE -> ILIKE substitution.""" + + def test_like_replaced(self): + result = _translated("SELECT * FROM person WHERE surname LIKE ?") + self.assertIn("ILIKE", result) + + def test_lowercase_like_replaced(self): + result = _translated("SELECT * FROM person WHERE surname like ?") + self.assertIn("ILIKE", result) + + def test_not_like_replaced(self): + result = _translated("SELECT * FROM person WHERE surname NOT LIKE ?") + self.assertIn("NOT ILIKE", result) + + def test_ilike_not_double_rewritten(self): + sql = "SELECT * FROM person WHERE surname ILIKE %s" + self.assertEqual(_translated(sql), sql) + + def test_like_word_boundary_not_in_identifier(self): + """LIKE as part of a longer identifier is not replaced.""" + sql = "SELECT likelihood FROM person" + self.assertEqual(_translated(sql), sql) + + def test_no_like_unchanged(self): + sql = "SELECT * FROM person WHERE surname = %s" + self.assertEqual(_translated(sql), sql) + + # ------------------------------------------------------------------------- # # TestExecuteSerialPrimaryKey From 7ecfe4b3a2128cf6372d90f60af8e660c7d5e87e Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Mon, 3 Aug 2026 13:32:09 -0700 Subject: [PATCH 085/156] Merge SharedPostgreSQL: prevent race on init; case insensitive like- #1008 --- SharedPostgreSQL/sharedpostgresql.gpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedPostgreSQL/sharedpostgresql.gpr.py b/SharedPostgreSQL/sharedpostgresql.gpr.py index dcff025c7..530acb828 100644 --- a/SharedPostgreSQL/sharedpostgresql.gpr.py +++ b/SharedPostgreSQL/sharedpostgresql.gpr.py @@ -24,7 +24,7 @@ name=_("SharedPostgreSQL"), name_accell=_("Shared _PostgreSQL Database"), description=_("Shared PostgreSQL Database"), - version = '0.1.17', + version = '0.1.18', gramps_target_version="6.1", status=STABLE, fname="sharedpostgresql.py", From c68e110969fd69ea6e346f37e3ae228ca5984462 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 2 Aug 2026 13:40:52 -0700 Subject: [PATCH 086/156] Add GOQLFilter addon Adds a gramps-object-query-language (GOQL) filter gramplet and a matching "MatchesExpression" custom-filter rule for each primary object type (Person, Family, Event, Place, Repository, Source, Citation, Media, Note). The gramplet is a multi-line where-expression editor (Tab completion, syntax highlighting, session-scoped history) that runs the expression as a real GenericFilter via view.generic_filter -- the same mechanism the built-in rule-based sidebar filter uses -- and can hand the same expression to Gramps' own EditFilter dialog to save it as an ordinary Custom Filter, reusable anywhere a Custom Filter is (other views' sidebar filters, reports, exports), not just in this gramplet. MatchesExpression pushes the expression down to SQL when the database is unproxied and DB-API-backed (SQLite, or the SharedPostgreSQL addon), falling back to per-object evaluation otherwise -- the same fast/slow split gramps-web-api's object_query resource makes. Requires the gramps-object-query-language package (pip install gramps-object-query-language); requires_mod in the .gpr registrations surfaces that as a Plugin Manager prerequisite. Co-Authored-By: Claude Sonnet 5 --- GOQLFilter/goql.gpr.py | 199 ++++++ GOQLFilter/goql.py | 623 ++++++++++++++++++ GOQLFilter/goql_completion.py | 142 ++++ GOQLFilter/goql_completion_popup.py | 336 ++++++++++ GOQLFilter/goql_highlight.py | 100 +++ GOQLFilter/goql_vocabulary.py | 37 ++ GOQLFilter/tests/__init__.py | 0 GOQLFilter/tests/test_goql.py | 593 +++++++++++++++++ GOQLFilter/tests/test_goql_completion.py | 125 ++++ .../tests/test_goql_completion_popup.py | 215 ++++++ GOQLFilter/tests/test_goql_highlight.py | 110 ++++ .../tests/test_integration_whereexprrule.py | 264 ++++++++ GOQLFilter/whereexprrule.gpr.py | 205 ++++++ GOQLFilter/whereexprrule.py | 302 +++++++++ 14 files changed, 3251 insertions(+) create mode 100644 GOQLFilter/goql.gpr.py create mode 100644 GOQLFilter/goql.py create mode 100644 GOQLFilter/goql_completion.py create mode 100644 GOQLFilter/goql_completion_popup.py create mode 100644 GOQLFilter/goql_highlight.py create mode 100644 GOQLFilter/goql_vocabulary.py create mode 100644 GOQLFilter/tests/__init__.py create mode 100644 GOQLFilter/tests/test_goql.py create mode 100644 GOQLFilter/tests/test_goql_completion.py create mode 100644 GOQLFilter/tests/test_goql_completion_popup.py create mode 100644 GOQLFilter/tests/test_goql_highlight.py create mode 100644 GOQLFilter/tests/test_integration_whereexprrule.py create mode 100644 GOQLFilter/whereexprrule.gpr.py create mode 100644 GOQLFilter/whereexprrule.py diff --git a/GOQLFilter/goql.gpr.py b/GOQLFilter/goql.gpr.py new file mode 100644 index 000000000..39475f4c8 --- /dev/null +++ b/GOQLFilter/goql.gpr.py @@ -0,0 +1,199 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Gramplets providing a gramps-object-query-language filter for each +primary object view.""" + +_HELP = "Addon:GrampsObjectQueryLanguage" +_AUTHORS = ["Douglas Blank"] +_AUTHORS_EMAIL = ["doug.blank@gmail.com"] + +register( + GRAMPLET, + id="Person GOQL Filter", + name=_("Person GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language person filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="PersonQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Person"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Family GOQL Filter", + name=_("Family GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language family filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="FamilyQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Family"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Event GOQL Filter", + name=_("Event GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language event filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="EventQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Event"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Place GOQL Filter", + name=_("Place GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language place filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="PlaceQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Place"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Repository GOQL Filter", + name=_("Repository GOQL Filter"), + description=_( + "Gramplet providing a gramps-object-query-language repository filter" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="RepositoryQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Repository"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Source GOQL Filter", + name=_("Source GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language source filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="SourceQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Source"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Citation GOQL Filter", + name=_("Citation GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language citation filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="CitationQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Citation"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Media GOQL Filter", + name=_("Media GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language media filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="MediaQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Media"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + GRAMPLET, + id="Note GOQL Filter", + name=_("Note GOQL Filter"), + description=_("Gramplet providing a gramps-object-query-language note filter"), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="goql.py", + height=260, + gramplet="NoteQueryFilter", + gramplet_title=_("GOQL Filter"), + navtypes=["Note"], + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) diff --git a/GOQLFilter/goql.py b/GOQLFilter/goql.py new file mode 100644 index 000000000..d9d8aa59f --- /dev/null +++ b/GOQLFilter/goql.py @@ -0,0 +1,623 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Gramplets providing a gramps-object-query-language (GOQL) filter for each +primary object view: a multi-line text area for a where-expression plus +Find / Reset / Define filter buttons, the same shape as core's sidebar +filters. + +"Find" compiles the expression, wraps it in a real ``GenericFilter`` (a +one-rule filter whose rule evaluates the compiled GOQL ``where`` AST via +``evaluate_where``), and drops it straight into ``view.generic_filter`` -- +the exact mechanism ``plugins/gramplet/filter.py`` uses for the built-in +rule-based sidebar filters, just fed from a compiled expression instead of a +rule-picker widget. "Define filter" hands the same expression to Gramps' +own ``EditFilter`` dialog as a single ``MatchesExpression`` rule (see +``whereexprrule.py``), so it can be named and saved as an ordinary Custom +Filter -- reusable anywhere a Custom Filter is, not just in this gramplet. + +The text area is a plain ``Enter``-inserts-a-newline ``Gtk.TextView`` (not a +``Gtk.Entry``) since where-expressions with ``and``/``or`` read better over +several lines; ``Ctrl+Return`` runs Find instead. Up/Down at the first/last +line of the buffer recall previous expressions from an in-memory, +per-gramplet-instance history (session-scoped -- not persisted to disk), +the same first/last-line-aware convention multiline REPL inputs use so +plain cursor movement inside a multi-line expression still works. +""" + +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import logging +import time +from typing import Any + +# ------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +# ------------------------------------------------------------------------- +from gi.repository import Gdk, Gtk + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.plug import Gramplet +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.filters import ( + CustomFilters, + GenericFilterFactory, + reload_custom_filters, +) +from gramps.gui.display import display_help, display_url +from gramps.gui.editors import EditFilter + +# ------------------------------------------------------------------------- +# +# gramps-object-query-language modules +# +# ------------------------------------------------------------------------- +try: + from gramps_object_query_language.query_lang import QueryLangError, compile_expr +except ImportError as err: + raise ImportError( + "GOQLFilter requires the gramps-object-query-language " + "package.\nInstall with: pip install gramps-object-query-language" + ) from err + +from whereexprrule import ( + CitationMatchesExpression, + EventMatchesExpression, + FamilyMatchesExpression, + MediaMatchesExpression, + NoteMatchesExpression, + PersonMatchesExpression, + PlaceMatchesExpression, + RepositoryMatchesExpression, + SourceMatchesExpression, +) +from goql_completion_popup import CompletionController +from goql_highlight import classify_tokens + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.gettext + +LOG = logging.getLogger(".GOQLFilter") + +# Minimum pixel height for the text area's scrolled window -- a floor, not +# the actual displayed size (the Paned in init() splits editor/buttons ~50% +# of whatever space is actually available; this just keeps the editor from +# collapsing to something unreadable if the user drags the divider all the +# way down). Not computed from font metrics (needs a realized widget), just +# a reasonable fixed default. +TEXT_AREA_HEIGHT = 90 + +# Cap on saved history entries -- self.gui.data round-trips through the +# gramplet's saved placement config (an .ini file) on every save, so an +# unbounded history would keep growing that file forever. +HISTORY_MAX_ENTRIES = 50 + +# Foreground colors for goql_highlight.classify_tokens' categories -- muted, +# mid-tone hex values chosen to stay legible on both light and dark GTK +# themes, since a plain Gtk.TextView has no adaptive-theme color mechanism +# to hook into (no runtime dark/light detection here). +HIGHLIGHT_COLORS = { + "keyword": "#7C3AED", + "string": "#15803D", + "number": "#B45309", + "constant-class": "#BE185D", + "operator": "#6B7280", +} + + +def _icon_button(icon_name, label_text): + """A ``Gtk.Button`` with an icon beside its label. + + Same construction ``_sidebarfilter.py``'s ``_init_interface`` uses for + its own "Reset" button (``edit-undo`` + label in an ``Gtk.Box``) -- + matched here for a consistent look, and reused for "Help". + """ + button = Gtk.Button() + box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4) + image = Gtk.Image() + image.set_from_icon_name(icon_name, Gtk.IconSize.BUTTON) + box.pack_start(image, False, False, 0) + box.pack_start(Gtk.Label(label=label_text), False, True, 0) + button.add(box) + return button + + +# ------------------------------------------------------------------------- +# +# QueryFilter +# +# ------------------------------------------------------------------------- +class QueryFilter(Gramplet): + """Base class for all GOQL filter gramplets.""" + + NAMESPACE: str = "" # e.g. "Person"; set by subclass + RULE_CLASS: Any = None # e.g. PersonMatchesExpression; set by subclass + + def init(self): + self.history = [] + self.history_index = None # None == live/current input, not browsing + self.history_draft = "" # the not-yet-submitted text, saved on history-back + + self.text_view = Gtk.TextView() + self.text_view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) + self.text_view.set_monospace(True) + self.text_buffer = self.text_view.get_buffer() + self.text_view.set_tooltip_text( + _("A gramps-object-query-language where-expression, e.g.\n") + + "gender == Person.MALE and 'Anderson' in primary_name.surname\n\n" + + _("Enter inserts a newline; Ctrl+Enter runs Find.\n") + + _("Up/Down at the first/last line recalls previous expressions.\n") + + _("Tab always completes -- it never inserts a tab character.") + ) + for tag_name, color in HIGHLIGHT_COLORS.items(): + self.text_buffer.create_tag(tag_name, foreground=color) + + self.completion = CompletionController( + self.text_view, get_namespace=lambda: self.NAMESPACE + ) + self.text_view.connect("key-press-event", self._on_key_press) + self.text_buffer.connect("changed", self._on_text_changed) + self.text_view.connect("button-press-event", self._on_textview_defocus) + self.text_view.connect("focus-out-event", self._on_textview_defocus) + + scroller = Gtk.ScrolledWindow() + scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scroller.set_shadow_type(Gtk.ShadowType.IN) + scroller.set_min_content_height(TEXT_AREA_HEIGHT) + scroller.add(self.text_view) + + # Same button shapes/icons as the built-in rule-based sidebar filter + # (`gui/filters/sidebar/_sidebarfilter.py`'s `_init_interface`): a + # plain mnemonic "Find" button, an icon+label "Reset", plain-text + # "Define filter", grouped into two ButtonBox rows the same way. + find_button = Gtk.Button.new_with_mnemonic(_("_Find")) + find_button.set_tooltip_text( + _("This updates the view with the current filter parameters.") + ) + find_button.connect("clicked", self.find_clicked) + + reset_button = _icon_button("edit-undo", _("Reset")) + reset_button.set_tooltip_text( + _("This resets the filter parameters to empty state.") + ) + reset_button.connect("clicked", self.reset_clicked) + + define_button = Gtk.Button(label=_("Define filter")) + define_button.set_tooltip_text( + _("This opens a dialog to save the current expression as a named filter.") + ) + define_button.connect("clicked", self.define_clicked) + + help_button = _icon_button("help-browser", _("Help")) + help_button.set_tooltip_text(_("Open this gramplet's help page in a browser.")) + help_button.connect("clicked", self.help_clicked) + + self.msg_label = Gtk.Label(label="") + self.msg_label.set_line_wrap(True) + self.msg_label.set_xalign(0) + + action_row = Gtk.ButtonBox() + action_row.set_layout(Gtk.ButtonBoxStyle.START) + action_row.set_spacing(6) + action_row.add(find_button) + action_row.add(reset_button) + + secondary_row = Gtk.ButtonBox() + secondary_row.set_layout(Gtk.ButtonBoxStyle.START) + secondary_row.set_spacing(6) + secondary_row.add(define_button) + secondary_row.add(help_button) + + bottom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + bottom_box.pack_start(action_row, False, False, 0) + bottom_box.pack_start(secondary_row, False, False, 0) + bottom_box.pack_start(self.msg_label, False, False, 0) + + # A Paned (same widget core's own gui/views/pageview.py uses for its + # sidebar/main-content split) rather than a plain Box with + # expand=True on the scroller: a Box has no notion of "50% of + # whatever's available," only "give the expanding child all + # leftover space" -- the text area would end up taller than half + # the gramplet on any reasonably tall placement. A Paned's handle + # is also user-draggable afterward, which a fixed split wouldn't be. + self.paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) + self.paned.set_border_width(6) + self.paned.pack1(scroller, True, True) + self.paned.pack2(bottom_box, False, True) + self._paned_position_set = False + self.paned.connect("size-allocate", self._init_paned_position) + + self.gui.get_container_widget().remove(self.gui.textview) + self.gui.get_container_widget().add(self.paned) + self.paned.show_all() + + def _init_paned_position(self, widget, allocation): + """Split the editor/buttons area ~50/50 the first time this pane is + actually sized. + + ``Gtk.Paned`` has no percentage-based position, only pixels, and + the real allocated height isn't known until the widget is + realized -- so this can't just be set once up front in ``init()``. + Runs exactly once (``self._paned_position_set``): after that, the + divider is the user's own drag to keep, not something to keep + resetting back to 50% on every later resize. + """ + if self._paned_position_set or allocation.height <= 1: + return + widget.set_position(allocation.height // 2) + self._paned_position_set = True + + def on_load(self): + """Restore history saved by a previous session. + + Called once, right after ``init()``, with ``self.gui.data`` already + populated from this gramplet's saved placement config (an .ini + file) -- the same mechanism core gramplets like + ``pedigreegramplet.py`` use for their own persisted options. Each + gramplet *instance* (Person filter, Family filter, ...) has its own + ``self.gui.data``, so histories stay independent automatically. + """ + self.history = [str(item) for item in self.gui.data] + + def on_save(self): + """Called on app/view shutdown, before ``self.gui.data`` is written + to disk -- see ``GrampletBar.on_delete``. + """ + self.gui.data = list(self.history) + + def _on_text_changed(self, _buffer): + self._apply_highlighting() + self.completion.on_buffer_changed() + + def _on_textview_defocus(self, _widget, _event): + """Close the completion popover on a click or focus-out -- shared + handler for both signals (``button-press-event``, + ``focus-out-event``), matching ``GrampyScript.py``'s + ``on_textview_click``/``on_editor_focus_out``. Never consumes the + event: a click still needs to move the cursor normally. + """ + self.completion.close() + return False + + def _apply_highlighting(self): + start, end = self.text_buffer.get_bounds() + for tag_name in HIGHLIGHT_COLORS: + self.text_buffer.remove_tag_by_name(tag_name, start, end) + source = self.text_buffer.get_text(start, end, False) + for start_line, start_col, end_line, end_col, category in classify_tokens( + source + ): + start_iter = self.text_buffer.get_iter_at_line_offset(start_line, start_col) + end_iter = self.text_buffer.get_iter_at_line_offset(end_line, end_col) + self.text_buffer.apply_tag_by_name(category, start_iter, end_iter) + + def _get_expr_text(self): + start, end = self.text_buffer.get_bounds() + return self.text_buffer.get_text(start, end, False).strip() + + def _set_expr_text(self, text): + self.text_buffer.set_text(text) + self.text_buffer.place_cursor(self.text_buffer.get_end_iter()) + + def _set_message(self, text): + self.msg_label.set_text(text) + + def _run_with_filter_progress(self, action): + """Run ``action()`` with Gramps' own filter-application phase + timings routed into this gramplet's message area, mirroring + ``gui/filters/sidebar/_sidebarfilter.py``'s ``clicked()``. + + ``GenericFilter.apply()`` (``gen/filters/_genericfilter.py``) + already reports "Prepare time: Xs" / "Apply time: Ys" via + ``user.notify(...)`` on every call -- but ``User._gui_print`` + (``gui/user.py``) only routes that to a widget if + ``uistate.filter_print_func`` is set; otherwise it falls through to + stdout, which is exactly why those lines showed up in a terminal + instead of this gramplet. Returns the collected phase messages. + """ + phase_msgs = [] + + def pump_events(): + while Gtk.events_pending(): + Gtk.main_iteration() + + def live_print(msg): + phase_msgs.append(msg) + self._set_message("\n".join(phase_msgs)) + pump_events() + + def live_step(): + pump_events() + + self.uistate.filter_print_func = live_print + self.uistate.filter_step_func = live_step + self.uistate.set_busy_cursor(True) + try: + action() + finally: + self.uistate.filter_print_func = None + self.uistate.filter_step_func = None + self.uistate.set_busy_cursor(False) + return phase_msgs + + def _filter_method_label(self, gfilter): + """ "SQL" if any rule in ``gfilter`` resolved a precomputed match set + (``MatchesExpression.prepare`` sets ``selected_handles`` only when + it pushed the expression down to SQL), else "Python evaluation". + """ + for rule in gfilter.get_rules(): + if getattr(rule, "selected_handles", None) is not None: + return _("SQL") + return _("Python evaluation") + + def _remember_history(self, expr): + """Append ``expr`` to history, skipping an immediate repeat.""" + if expr and (not self.history or self.history[-1] != expr): + self.history.append(expr) + del self.history[:-HISTORY_MAX_ENTRIES] + self.history_index = None + + def _history_back(self): + if not self.history: + return + if self.history_index is None: + self.history_draft = self._get_expr_text() + self.history_index = len(self.history) - 1 + elif self.history_index > 0: + self.history_index -= 1 + else: + return # already at the oldest entry + self._set_expr_text(self.history[self.history_index]) + + def _history_forward(self): + if self.history_index is None: + return # not browsing + if self.history_index < len(self.history) - 1: + self.history_index += 1 + self._set_expr_text(self.history[self.history_index]) + else: + self.history_index = None + self._set_expr_text(self.history_draft) + + def _on_key_press(self, _widget, event): + # Completion first: when its popover is open, Up/Down/Enter/Escape + # navigate/accept/dismiss it rather than falling through to history + # navigation or a newline below -- see CompletionController's own + # key-press dispatch. + if self.completion.on_key_press(event): + return True + + ctrl = bool(event.state & Gdk.ModifierType.CONTROL_MASK) + + if event.keyval == Gdk.KEY_Tab: + # Tab is always completion, never a literal tab/space insert: + # self.completion.on_key_press already tried above and + # returned False here only because there was nothing + # completable -- still consume the key rather than falling + # back to GTK's default (inserting a tab character). + return True + + if ctrl and event.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): + self.find_clicked(_widget) + return True # don't also insert a newline + + if not ctrl and event.keyval in (Gdk.KEY_Up, Gdk.KEY_Down): + cursor = self.text_buffer.get_iter_at_mark(self.text_buffer.get_insert()) + at_first_line = cursor.get_line() == 0 + at_last_line = cursor.get_line() == self.text_buffer.get_line_count() - 1 + if event.keyval == Gdk.KEY_Up and at_first_line: + self._history_back() + return True + if event.keyval == Gdk.KEY_Down and at_last_line: + self._history_forward() + return True + + return False # let GTK handle it normally + + def _hide_quick_search_bar(self): + """Close the view's own quick-search bar, if open. + + ``ListView.build_tree()`` only reads ``generic_filter`` when its + quick-search bar (the small text-search box built into every list + view, independent of this gramplet) is hidden -- otherwise it uses + *that* bar's own filter instead and ``generic_filter`` is silently + ignored. The bar auto-hides when the view's Sidebar pane is toggled + on, but this gramplet may just as well be docked in the bottombar, + so hide it explicitly rather than depending on where the user put + the gramplet. + """ + search_bar = getattr(self.gui.view, "search_bar", None) + if search_bar is not None and search_bar.is_visible(): + search_bar.hide() + + def _build_generic_filter(self, expr): + """A ``GenericFilter`` for ``expr``, or ``None`` if it fails to compile. + + Validates ``expr`` up front so a bad expression reports a clear error + here rather than silently matching nothing -- ``RULE_CLASS.prepare()`` + recompiles it a second time when the filter is applied, since a + ``Rule`` only ever receives its arguments as plain strings. + """ + try: + compile_expr(self.NAMESPACE, expr) + except QueryLangError as err: + self._set_message(str(err)) + return None + gfilter = GenericFilterFactory(self.NAMESPACE)() + gfilter.add_rule(self.RULE_CLASS([expr])) + return gfilter + + def find_clicked(self, _obj): + expr = self._get_expr_text() + self._set_message("") + if not expr: + self.reset_clicked(_obj) + return + gfilter = self._build_generic_filter(expr) + if gfilter is None: + return + self._remember_history(expr) + phase_msgs = [] + try: + self._hide_quick_search_bar() + self.gui.view.generic_filter = gfilter + + def do_build_tree(): + self.gui.view.build_tree() + + phase_msgs = self._run_with_filter_progress(do_build_tree) + except Exception as err: # never let a click silently fail + LOG.exception("GOQL Filter gramplet: Find failed") + self._set_message(_("Error applying filter: %s") % err) + return + model = self.gui.view.model + summary = _("Showing %(shown)d of %(total)d (%(method)s)") % { + "shown": model.displayed(), + "total": model.total(), + "method": self._filter_method_label(gfilter), + } + self._set_message("\n".join(phase_msgs + [summary])) + + def reset_clicked(self, _obj): + self._set_expr_text("") + try: + self._hide_quick_search_bar() + self.gui.view.generic_filter = None + self.gui.view.build_tree() + except Exception as err: + LOG.exception("GOQL Filter gramplet: Reset failed") + self._set_message(_("Error resetting filter: %s") % err) + return + self._set_message("") + + def define_clicked(self, _obj): + expr = self._get_expr_text() + if not expr: + self._set_message(_("Enter an expression first")) + return + try: + compile_expr(self.NAMESPACE, expr) + except QueryLangError as err: + self._set_message(str(err)) + return + self._set_message("") + self._remember_history(expr) + + gfilter = GenericFilterFactory(self.NAMESPACE)() + gfilter.add_rule(self.RULE_CLASS([expr])) + comment = _("Created by the GOQL Filter gramplet on {today}").format( + today=time.strftime("%Y-%m-%d", time.localtime()) + ) + gfilter.set_comment(comment) + + EditFilter( + self.NAMESPACE, + self.dbstate, + self.uistate, + [], + gfilter, + CustomFilters, + selection_callback=self._filter_defined, + ) + + def _filter_defined(self, filterdb, _filter_name): + filterdb.save() + reload_custom_filters() + self.uistate.emit("filters-changed", (self.NAMESPACE,)) + + def help_clicked(self, _obj): + """Open this gramplet's registered ``help_url`` in a browser. + + ``self.gui.help_url`` is set from the ``.gpr.py`` registration's + ``help_url=`` (``GuiGramplet.__init__``, ``gui/widgets/ + grampletpane.py``) -- the same attribute the built-in "Help" menu + item on every gramplet tab already reads, via the same + ``http(s)://`` vs. wiki-page-name dispatch used there + (``grampletpane.py``/``grampletbar.py``'s own right-click "Help"). + """ + help_url = getattr(self.gui, "help_url", None) + if not help_url: + return + if help_url.startswith(("http://", "https://")): + display_url(help_url) + else: + display_help(help_url) + + +# ------------------------------------------------------------------------- +# +# Per-type gramplets +# +# ------------------------------------------------------------------------- +class PersonQueryFilter(QueryFilter): + NAMESPACE = "Person" + RULE_CLASS = PersonMatchesExpression + + +class FamilyQueryFilter(QueryFilter): + NAMESPACE = "Family" + RULE_CLASS = FamilyMatchesExpression + + +class EventQueryFilter(QueryFilter): + NAMESPACE = "Event" + RULE_CLASS = EventMatchesExpression + + +class PlaceQueryFilter(QueryFilter): + NAMESPACE = "Place" + RULE_CLASS = PlaceMatchesExpression + + +class RepositoryQueryFilter(QueryFilter): + NAMESPACE = "Repository" + RULE_CLASS = RepositoryMatchesExpression + + +class SourceQueryFilter(QueryFilter): + NAMESPACE = "Source" + RULE_CLASS = SourceMatchesExpression + + +class CitationQueryFilter(QueryFilter): + NAMESPACE = "Citation" + RULE_CLASS = CitationMatchesExpression + + +class MediaQueryFilter(QueryFilter): + NAMESPACE = "Media" + RULE_CLASS = MediaMatchesExpression + + +class NoteQueryFilter(QueryFilter): + NAMESPACE = "Note" + RULE_CLASS = NoteMatchesExpression diff --git a/GOQLFilter/goql_completion.py b/GOQLFilter/goql_completion.py new file mode 100644 index 000000000..005cf2989 --- /dev/null +++ b/GOQLFilter/goql_completion.py @@ -0,0 +1,142 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""GOQL-specific completion source for ``goql_completion_popup``'s +``CompletionController``. + +Kept free of GTK imports, mirroring ``GrampyScript/completion.py``'s own +"testable without a display" convention -- ``goql.py`` is responsible for +turning a ``Gtk.TextBuffer`` cursor position into ``(source, line, column)``. + +Deliberately not jedi-based, unlike GrampyScript's completion engine: a +where-expression is never executed, has no live namespace of real Python +objects to introspect, and its grammar is a small closed set (see +``gramps_object_query_language.query_lang``'s module docstring) -- general +Python completion would suggest names (arbitrary methods, ``import``, ...) +that are simply invalid here. This only ever offers two things, matching +what was actually asked for: + +- At the top level (no ``.`` immediately before the word being typed): the + current namespace's own vocabulary -- flat column names, relationship + names (``birth``, ``father``, ...), collection names (``children``, + ``events``, ...), the where-expression keywords (``and``/``or``/``not``/ + ``in``/``like``/``Date``/``exists``/``count``), the comparison operators + (``==``/``!=``/``<``/``<=``/``>``/``>=``) -- included even though most are + a single character and so only ever surface with an empty prefix (Tab on + its own), not because they're likely to be *typed* out via completion -- + and the constant class names themselves (``Person``, ``EventType``, ...) + so typing far enough to reach one and then ``.`` is a smooth continuation. +- Right after ``ClassName.``, where ``ClassName`` is one of the constant + classes ``query_lang.py`` recognizes on the value side of a comparison + (``Person.MALE``, ``EventType.BIRTH``, ...): that class's own ALL_CAPS + int constants. + +Anything else (e.g. completing a nested JSON field inside +``primary_name.``) returns no completions -- out of scope for now, not a +silent failure to fix. +""" + +from __future__ import annotations + +import re +from typing import Any, Dict, List, Set + +# `_RELATIONSHIPS`/`_COLLECTIONS` (query.py) and `_CONSTANT_CLASSES`/ +# `_CONSTANTS` (query_lang.py) are module-private -- there is no public way +# to *enumerate* every relationship/collection/constant name for a spec +# (only to resolve one given name, via the public `resolve_collection`/ +# `resolve_column_path`). Reaching into them here is a deliberate, narrow +# exception for exactly this reason, not an oversight; if +# gramps-object-query-language grows a public enumeration API, switch to it. +from gramps_object_query_language.query import _COLLECTIONS, _RELATIONSHIPS +from gramps_object_query_language.query_lang import ( + QueryLangError, + _CONSTANT_CLASSES, + _CONSTANTS, + resolve_namespace, +) + +from goql_vocabulary import COMPARISON_OPERATORS, KEYWORDS + +_DOTTED_RE = re.compile(r"([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z0-9_]*)$") +_WORD_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$") + + +def _text_before_cursor(source: str, line: int, column: int) -> str: + """``source`` truncated to the cursor position. + + ``line`` is 1-indexed, ``column`` is a 0-indexed character offset + within that line -- the same convention ``CompletionController`` uses + (``GrampyScript/completion_popup.py``'s ``_cursor_line_column``, + matching ``Gtk.TextIter.get_line() + 1`` / ``get_line_offset()``). + """ + lines = source.split("\n") + index = max(0, min(line - 1, len(lines) - 1)) if lines else 0 + before_lines = lines[:index] + current_line = lines[index] if lines else "" + return "\n".join(before_lines + [current_line[:column]]) + + +def _top_level_names(namespace: str) -> Set[str]: + try: + spec = resolve_namespace(namespace) + except QueryLangError: + return set() + names: Set[str] = set(spec.columns) + names.update(_RELATIONSHIPS.get(spec.table, {}).keys()) + names.update(_COLLECTIONS.get(spec.table, {}).keys()) + names.update(KEYWORDS) + names.update(COMPARISON_OPERATORS) + names.update(_CONSTANT_CLASSES.keys()) + return names + + +def _constant_names(class_name: str) -> Set[str]: + return set(_CONSTANTS.get(class_name, {}).keys()) + + +def get_completion_items( + source: str, line: int, column: int, namespace: str +) -> List[Dict[str, Any]]: + """Candidate completions at ``(line, column)`` in ``source``, for the + given GOQL ``namespace`` ("Person", "Family", ...). + + Same return shape as ``GrampyScript/completion.py``'s + ``get_completion_items`` -- ``{"name": ..., "complete": ..., "cursor_offset": + 0}`` dicts -- so ``goql_completion_popup.CompletionController`` (adapted + from ``GrampyScript/completion_popup.py``) can drive either unchanged. + Always flat: no attempt to parse the surrounding expression's grammar, + only what immediately precedes the word being completed. + """ + text_before = _text_before_cursor(source, line, column) + dotted = _DOTTED_RE.search(text_before) + if dotted: + class_name, prefix = dotted.group(1), dotted.group(2) + names = _constant_names(class_name) + else: + word = _WORD_RE.search(text_before) + prefix = word.group(0) if word else "" + names = _top_level_names(namespace) + + matches = sorted(name for name in names if name.startswith(prefix)) + return [ + {"name": name, "complete": name[len(prefix) :], "cursor_offset": 0} + for name in matches + ] diff --git a/GOQLFilter/goql_completion_popup.py b/GOQLFilter/goql_completion_popup.py new file mode 100644 index 000000000..dbcb35877 --- /dev/null +++ b/GOQLFilter/goql_completion_popup.py @@ -0,0 +1,336 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2025 Doug Blank +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""A Tab-triggered, live-filtering completion popover for a ``Gtk.TextView``. + +Vendored from the ``GrampyScript`` addon's ``completion_popup.py`` +(``CompletionController``), which is deliberately GTK-only and +completion-source-agnostic already -- its own docstring says it's "kept as +a standalone controller... so it can be driven directly against a plain +Gtk.TextView", touching its jedi-based backend through exactly one import +(``from completion import get_completion_items``). That's the only line +changed here: swapped for ``goql_completion``'s namespace-aware, non-jedi +source (see that module's docstring for why a where-expression can't reuse +GrampyScript's actual completion engine, only its popup). + +Named differently from GrampyScript's own ``completion_popup.py``/ +``completion.py`` on purpose -- if both addons are ever installed at once, +Gramps' plugin loader puts each addon's own directory on ``sys.path``, and +a bare ``import completion_popup`` here could resolve to whichever one +happened to be imported first instead of this file (the same +namespace-collision hazard as Mantis 12691, just at the addon-to-addon +level instead of within one addon). + +Two further deltas from the vendored original, both because this editor's +Tab key has no fallback behavior to protect (GrampyScript's Tab inserts 4 +spaces when nothing is completable; this one never inserts anything): + +- ``_is_completable_context`` always returns True here -- GrampyScript + gates completion on the preceding character (alnum/``_``/``.``/``]``) so + Tab's whitespace-insert fallback doesn't fire in a confusing spot; this + editor has no such fallback to protect, and "Tab on an empty buffer + shows every top-level name for the current namespace" is exactly the + wanted behavior, not an edge case to guard against. +- Callers are expected to unconditionally consume ``Gdk.KEY_Tab`` + themselves (return ``True``) regardless of what ``on_key_press`` reports, + rather than falling back to inserting a tab character the way + GrampyScript's own key handler does. + +Wiring it into a host widget requires forwarding four things: + textview "key-press-event" -> controller.on_key_press(event) + (if it returns True, treat the event + as handled and stop further processing) + buffer "changed" -> controller.on_buffer_changed() + textview "button-press-event"/"focus-out-event" -> controller.close() +""" + +import logging + +from gi.repository import Gdk, Gtk + +from goql_completion import get_completion_items + +_LOG = logging.getLogger(".GOQLFilter.completion") + +_NAVIGATION_KEYS = ( + Gdk.KEY_Left, + Gdk.KEY_Right, + Gdk.KEY_Home, + Gdk.KEY_End, + Gdk.KEY_Page_Up, + Gdk.KEY_Page_Down, +) + + +class CompletionController: + def __init__(self, textview, get_namespace): + """ + `textview`: the Gtk.TextView to attach completion to. + `get_namespace`: zero-arg callable returning the current GOQL + namespace string ("Person", "Family", ...) for + `get_completion_items()`; called fresh on every request so it + always reflects whichever gramplet/rule this controller is + attached to. + """ + self.textview = textview + self.buffer = textview.get_buffer() + self.get_namespace = get_namespace + self.popover = None + self.listbox = None + self.scrolled = None + self.items = [] + self.selected_index = 0 + + # ---- public event entry points ----------------------------------- + + def on_key_press(self, event): + """Return True if the event was consumed and should not be + processed any further by the caller.""" + try: + return self._on_key_press(event) + except Exception: + # Never let a bug here swallow the keypress entirely -- that + # would leave GTK's own default handler to run instead, which + # looks like "completion silently does nothing." Log and fall + # back to "not handled" instead. + _LOG.exception("completion on_key_press failed") + self.close() + return False + + def _on_key_press(self, event): + keyval = event.keyval + _LOG.debug( + "on_key_press keyval=%s open=%s", Gdk.keyval_name(keyval), self.is_open() + ) + if keyval == Gdk.KEY_Tab: + if self.is_open(): + self.accept() + return True + return self.trigger() + if self.is_open(): + if keyval == Gdk.KEY_Up: + self.move_selection(-1) + return True + if keyval == Gdk.KEY_Down: + self.move_selection(1) + return True + if keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): + self.accept() + return True + if keyval == Gdk.KEY_Escape: + self.close() + return True + if keyval in _NAVIGATION_KEYS: + # The cursor is about to move out from under the popover; + # let it move normally, just stop completing at this spot. + self.close() + return False + return False + + def on_buffer_changed(self): + if not self.is_open(): + return + try: + self.refresh() + except Exception: + _LOG.exception("completion refresh failed") + self.close() + + def is_open(self): + return self.popover is not None + + # ---- core ----------------------------------------------------------- + + def _cursor_iter(self): + return self.buffer.get_iter_at_mark(self.buffer.get_insert()) + + def _cursor_line_column(self): + it = self._cursor_iter() + return it.get_line() + 1, it.get_line_offset() + + def _word_prefix(self): + it = self._cursor_iter() + start = it.copy() + while start.backward_char(): + ch = start.get_char() + if ch.isalnum() or ch == "_": + continue + start.forward_char() + break + return self.buffer.get_text(start, it, True) + + def _is_completable_context(self): + # Always True here -- see this module's docstring for why, unlike + # GrampyScript's version, this editor has no whitespace-insert + # fallback to protect Tab from firing into. + return True + + def _compute_items(self): + source = self.buffer.get_text( + self.buffer.get_start_iter(), self.buffer.get_end_iter(), True + ) + line, column = self._cursor_line_column() + prefix = self._word_prefix() + _LOG.debug( + "computing completions at line=%s column=%s prefix=%r", line, column, prefix + ) + try: + namespace = self.get_namespace() + items = get_completion_items(source, line, column, namespace) + except Exception: + _LOG.exception("building completion items failed") + return [] + if not prefix.startswith("_"): + items = [item for item in items if not item["name"].startswith("_")] + _LOG.debug( + "found %d completion(s): %s", len(items), [i["name"] for i in items[:10]] + ) + return items + + def trigger(self): + """Try to complete at the cursor. Returns True if there was + something completable to show. A single match is inserted + directly instead of opening a popover with one row in it; + multiple matches open the popover as usual.""" + if not self._is_completable_context(): + return False + items = self._compute_items() + if not items: + _LOG.debug("trigger: no completions") + return False + self.items = items + self.selected_index = 0 + if len(items) == 1: + _LOG.debug( + "trigger: single match, inserting directly: %s", items[0]["name"] + ) + self.accept() + return True + self._open_popover() + return True + + def refresh(self): + """Recompute matches for an already-open popover, following the + cursor as the user keeps typing. Closes if nothing matches + anymore.""" + items = self._compute_items() + if not items: + self.close() + return + self.items = items + self.selected_index = min(self.selected_index, len(items) - 1) + self._rebuild_listbox() + self._reposition() + + def move_selection(self, delta): + if not self.items: + return + self.selected_index = max( + 0, min(len(self.items) - 1, self.selected_index + delta) + ) + self._update_row_selection() + + def accept(self): + if self.items: + item = self.items[self.selected_index] + self.buffer.insert(self._cursor_iter(), item["complete"]) + offset = item.get("cursor_offset", 0) + if offset: + it = self._cursor_iter() + it.backward_chars(offset) + self.buffer.place_cursor(it) + self.close() + + def close(self): + if self.popover is not None: + self.popover.destroy() + self.popover = None + self.listbox = None + self.scrolled = None + self.items = [] + self.selected_index = 0 + + # ---- widget building -------------------------------------------------- + + def _open_popover(self): + self.popover = Gtk.Popover() + self.popover.set_relative_to(self.textview) + self.popover.set_modal(False) + self.popover.set_position(Gtk.PositionType.BOTTOM) + + self.scrolled = Gtk.ScrolledWindow() + self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + self.scrolled.set_max_content_height(200) + self.scrolled.set_propagate_natural_height(True) + + self.listbox = Gtk.ListBox() + self.listbox.set_activate_on_single_click(True) + self.listbox.connect("row-activated", self._on_row_activated) + self.scrolled.add(self.listbox) + self.popover.add(self.scrolled) + + self._rebuild_listbox() + self.popover.show_all() + self._reposition() + self.popover.popup() + + def _rebuild_listbox(self): + for child in self.listbox.get_children(): + self.listbox.remove(child) + for item in self.items: + label = Gtk.Label(label=item["name"], xalign=0) + label.set_margin_start(6) + label.set_margin_end(6) + row = Gtk.ListBoxRow() + row.add(label) + self.listbox.add(row) + self.listbox.show_all() + self._update_row_selection() + + def _update_row_selection(self): + row = self.listbox.get_row_at_index(self.selected_index) + if row is not None: + self.listbox.select_row(row) + self._scroll_to_row(row) + + def _scroll_to_row(self, row): + alloc = row.get_allocation() + adj = self.scrolled.get_vadjustment() + if alloc.y < adj.get_value(): + adj.set_value(alloc.y) + elif alloc.y + alloc.height > adj.get_value() + adj.get_page_size(): + adj.set_value(alloc.y + alloc.height - adj.get_page_size()) + + def _reposition(self): + rect = self.textview.get_iter_location(self._cursor_iter()) + x, y = self.textview.buffer_to_window_coords( + Gtk.TextWindowType.WIDGET, rect.x, rect.y + ) + pointing = Gdk.Rectangle() + pointing.x = x + pointing.y = y + pointing.width = 1 + pointing.height = rect.height + self.popover.set_pointing_to(pointing) + + def _on_row_activated(self, listbox, row): + self.selected_index = row.get_index() + self.accept() diff --git a/GOQLFilter/goql_highlight.py b/GOQLFilter/goql_highlight.py new file mode 100644 index 000000000..d7551e816 --- /dev/null +++ b/GOQLFilter/goql_highlight.py @@ -0,0 +1,100 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Token-span classification for GOQL where-expression syntax highlighting. + +Kept free of GTK imports -- same "testable without a display" convention as +``goql_completion.py`` -- ``goql.py`` is responsible for turning a span into +a ``Gtk.TextTag`` range. + +Uses Python's own ``tokenize`` module rather than a hand-written lexer: a +where-expression's tokens (names, string/number literals, comparison +operators) are a strict subset of Python's own, so there's no new grammar +to maintain here -- just a classification on top of what ``tokenize`` +already produces. This is lexical only (keyword/string/number/operator/ +known-constant-class recognition from token text alone), not a real parse +-- it never rejects anything ``query_lang.py`` wouldn't also accept or +reject on its own; ``compile_expr``'s own error message is still the +source of truth for whether an expression is valid. +""" + +from __future__ import annotations + +import io +import tokenize +from typing import Iterator, Tuple + +from gramps_object_query_language.query_lang import _CONSTANT_CLASSES + +from goql_vocabulary import COMPARISON_OPERATORS, KEYWORDS + +# (start_line, start_col, end_line, end_col, category) -- 0-indexed lines, +# matching Gtk.TextIter's own convention (tokenize's rows are 1-indexed). +Span = Tuple[int, int, int, int, str] + + +def classify_tokens(source: str) -> Iterator[Span]: + """Yield a ``Span`` for each highlightable token in ``source``. + + Incomplete/invalid source -- the normal state of this buffer while + still being typed, not an error case -- is handled by + ``_tokenize_best_effort``: whatever ``tokenize`` managed to produce + before hitting the unparseable part is still yielded. + """ + for tok in _tokenize_best_effort(source): + category = _classify(tok) + if category is None: + continue + start_line, start_col = tok.start + end_line, end_col = tok.end + yield (start_line - 1, start_col, end_line - 1, end_col, category) + + +def _classify(tok) -> str | None: + if tok.type == tokenize.STRING: + return "string" + if tok.type == tokenize.NUMBER: + return "number" + if tok.type == tokenize.NAME: + if tok.string in KEYWORDS: + return "keyword" + if tok.string in _CONSTANT_CLASSES: + return "constant-class" + return None + if tok.type == tokenize.OP and tok.string in COMPARISON_OPERATORS: + return "operator" + return None + + +def _tokenize_best_effort(source: str): + """``tokenize.generate_tokens`` raises on unterminated strings, + unbalanced brackets, or an expression that just stops mid-line -- all + routine while typing, not something to surface as a highlighting + failure. Whatever was already yielded before the exception is kept + (the loop below appends token-by-token, so a raise partway through + only truncates the tail, not the whole result). + """ + tokens = [] + try: + for tok in tokenize.generate_tokens(io.StringIO(source).readline): + tokens.append(tok) + except Exception: + pass + return tokens diff --git a/GOQLFilter/goql_vocabulary.py b/GOQLFilter/goql_vocabulary.py new file mode 100644 index 000000000..1c3e88cc2 --- /dev/null +++ b/GOQLFilter/goql_vocabulary.py @@ -0,0 +1,37 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Shared where-expression token vocabulary. + +The small, fixed set of non-column tokens a where-expression can use -- +``goql_completion.py`` (Tab completion) and ``goql_highlight.py`` (syntax +coloring) both need this same list and previously each kept their own +copy, which is exactly the kind of thing that quietly drifts apart when +one gets updated and the other doesn't. One source of truth instead. + +``and``/``or``/``not``/``in`` are real Python keywords (and thus real +where_expr operators); ``like``/``Date``/``exists``/``count`` are the +whitelisted function-call forms ``gramps_object_query_language.query_lang`` +recognizes -- see that module's docstring. +""" + +KEYWORDS = frozenset({"and", "or", "not", "in", "like", "Date", "exists", "count"}) + +COMPARISON_OPERATORS = frozenset({"==", "!=", "<", "<=", ">", ">="}) diff --git a/GOQLFilter/tests/__init__.py b/GOQLFilter/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/GOQLFilter/tests/test_goql.py b/GOQLFilter/tests/test_goql.py new file mode 100644 index 000000000..78fde27c0 --- /dev/null +++ b/GOQLFilter/tests/test_goql.py @@ -0,0 +1,593 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Mocked unit tests for the ``QueryFilter`` gramplet's click handlers. + +No live Gramps window, no database on disk -- ``self.gui``/``self.dbstate``/ +``self.uistate`` are replaced with mocks (``PersonQueryFilter.__new__`` +bypasses ``Gramplet.__init__``, the same pattern +``Form/tests/test_editform_save_guards.py`` uses to avoid needing a running +main window). ``compile_expr``/``GenericFilterFactory`` run for real -- +they're pure/fast and need no display. + +Run with:: + + python3 -m unittest GOQLFilter.tests.test_goql -v +""" + +import os +import sys +import types +import unittest +from unittest.mock import MagicMock, patch + +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + import gi # noqa: F401 + from gi.repository import Gdk +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) + +try: + import goql as goql_gramplet +except ImportError as exc: + raise unittest.SkipTest( + "goql import failed (likely missing gramps-object-query-language): %s" % exc + ) + + +def _make_gramplet(initial_text=""): + """A ``PersonQueryFilter`` with only the attributes its click handlers + touch -- bypasses ``Gramplet.__init__``, which wants a live + ``GuiGramplet``/``dbstate``/``uistate`` and builds real widgets against + a running Gramps window. ``_get_expr_text``/``_set_expr_text`` stand in + for the real ``Gtk.TextBuffer``-backed versions -- what they read from + and write to is exactly the seam ``find_clicked``/``reset_clicked``/ + ``define_clicked``/history navigation go through. + """ + gramplet = goql_gramplet.PersonQueryFilter.__new__(goql_gramplet.PersonQueryFilter) + text = {"value": initial_text} + gramplet._get_expr_text = MagicMock(side_effect=lambda: text["value"].strip()) + gramplet._set_expr_text = MagicMock( + side_effect=lambda t: text.__setitem__("value", t) + ) + gramplet.msg_label = MagicMock() + gramplet.dbstate = MagicMock() + gramplet.uistate = MagicMock() + gramplet.gui = MagicMock() + gramplet.history = [] + gramplet.history_index = None + gramplet.history_draft = "" + gramplet.completion = MagicMock() + gramplet.completion.on_key_press.return_value = False + return gramplet + + +def _key_event(keyval, ctrl=False): + return types.SimpleNamespace( + keyval=keyval, + state=(Gdk.ModifierType.CONTROL_MASK if ctrl else Gdk.ModifierType(0)), + ) + + +# ------------------------------------------------------------ +# +# DefineFilterCallbackTest +# +# ------------------------------------------------------------ +class DefineFilterCallbackTest(unittest.TestCase): + """Regression test: interactively, "Define filter" -> OK raised + ``TypeError: QueryFilter._filter_defined() missing 2 required + positional arguments: 'filterdb' and '_filter_name'``. + + Cause: ``define_clicked`` passed ``_filter_defined`` to ``EditFilter``'s + ``update`` parameter, which ``EditFilter.on_ok_clicked`` calls with zero + arguments (``self.update()``). The two-argument call + (``self.selection_callback(self.filterdb, self.filter.get_name())``) + is a separate parameter, ``selection_callback`` -- see + ``gramps/gui/editors/filtereditor.py``. Fixed by passing + ``_filter_defined`` as ``selection_callback`` instead. + """ + + def test_define_clicked_uses_selection_callback_not_update(self): + gramplet = _make_gramplet("gender == Person.MALE") + + with patch.object(goql_gramplet, "EditFilter") as mock_edit_filter: + gramplet.define_clicked(None) + + mock_edit_filter.assert_called_once() + _args, kwargs = mock_edit_filter.call_args + # Bound methods aren't identical objects across separate attribute + # accesses even for the same instance+function, so compare by + # equality (bound methods implement __eq__), not identity. + self.assertEqual(kwargs.get("selection_callback"), gramplet._filter_defined) + self.assertIsNone(kwargs.get("update")) + + def test_filter_defined_matches_editfilter_call_signature(self): + """``EditFilter.on_ok_clicked`` calls + ``self.selection_callback(self.filterdb, self.filter.get_name())`` + -- ``_filter_defined`` must accept exactly that shape and use it to + persist + reload the custom filter list. + """ + gramplet = _make_gramplet() + filterdb = MagicMock() + + with patch.object(goql_gramplet, "reload_custom_filters") as mock_reload: + gramplet._filter_defined(filterdb, "My Filter") + + filterdb.save.assert_called_once() + mock_reload.assert_called_once() + gramplet.uistate.emit.assert_called_once_with("filters-changed", ("Person",)) + + def test_define_clicked_reports_compile_error_without_opening_dialog(self): + gramplet = _make_gramplet("this is not valid GOQL !!") + + with patch.object(goql_gramplet, "EditFilter") as mock_edit_filter: + gramplet.define_clicked(None) + + mock_edit_filter.assert_not_called() + gramplet.msg_label.set_text.assert_called() + + +# ------------------------------------------------------------ +# +# FindResetTest +# +# ------------------------------------------------------------ +class FindResetTest(unittest.TestCase): + """Happy-path coverage for the Find/Reset mechanism itself: setting + ``view.generic_filter`` and calling ``view.build_tree()`` -- the same + two calls core's own ``plugins/gramplet/filter.py`` uses. + """ + + def test_find_clicked_sets_generic_filter_and_rebuilds(self): + gramplet = _make_gramplet("gender == Person.MALE") + gramplet.gui.view.search_bar.is_visible.return_value = False + + gramplet.find_clicked(None) + + self.assertIsNotNone(gramplet.gui.view.generic_filter) + gramplet.gui.view.build_tree.assert_called_once() + + def test_find_clicked_reports_compile_error_without_touching_view(self): + gramplet = _make_gramplet("this is not valid GOQL !!") + + gramplet.find_clicked(None) + + gramplet.gui.view.build_tree.assert_not_called() + gramplet.msg_label.set_text.assert_called() + + def test_find_clicked_with_empty_expression_resets_instead(self): + gramplet = _make_gramplet(" ") + gramplet.gui.view.search_bar.is_visible.return_value = False + + gramplet.find_clicked(None) + + gramplet._set_expr_text.assert_called_once_with("") + self.assertIsNone(gramplet.gui.view.generic_filter) + gramplet.gui.view.build_tree.assert_called_once() + + def test_reset_clicked_clears_filter_and_rebuilds(self): + gramplet = _make_gramplet() + gramplet.gui.view.search_bar.is_visible.return_value = False + + gramplet.reset_clicked(None) + + gramplet._set_expr_text.assert_called_once_with("") + self.assertIsNone(gramplet.gui.view.generic_filter) + gramplet.gui.view.build_tree.assert_called_once() + + def test_hide_quick_search_bar_hides_when_visible(self): + gramplet = _make_gramplet() + gramplet.gui.view.search_bar.is_visible.return_value = True + + gramplet._hide_quick_search_bar() + + gramplet.gui.view.search_bar.hide.assert_called_once() + + def test_hide_quick_search_bar_leaves_hidden_bar_alone(self): + gramplet = _make_gramplet() + gramplet.gui.view.search_bar.is_visible.return_value = False + + gramplet._hide_quick_search_bar() + + gramplet.gui.view.search_bar.hide.assert_not_called() + + def test_hide_quick_search_bar_tolerates_a_view_without_one(self): + gramplet = _make_gramplet() + del gramplet.gui.view.search_bar # e.g. a non-ListView pageview + + gramplet._hide_quick_search_bar() # must not raise + + +def _make_gramplet_for_keypress(cursor_line=0, line_count=1): + """A gramplet with a mocked ``text_buffer`` reporting a fixed cursor + line / line count, and mocked ``find_clicked``/``_history_back``/ + ``_history_forward`` -- enough to test ``_on_key_press``'s dispatch in + isolation, without constructing a real ``Gtk.TextView`` (needs a real + display connection to construct at all, which the documented test + invocation's ``GDK_BACKEND=-`` deliberately does without -- see + ``HistoryNavigationTest`` for the text-content-level coverage this + dispatch-only test doesn't duplicate). + """ + gramplet = goql_gramplet.PersonQueryFilter.__new__(goql_gramplet.PersonQueryFilter) + cursor_iter = MagicMock() + cursor_iter.get_line.return_value = cursor_line + gramplet.text_buffer = MagicMock() + gramplet.text_buffer.get_iter_at_mark.return_value = cursor_iter + gramplet.text_buffer.get_line_count.return_value = line_count + gramplet.find_clicked = MagicMock() + gramplet._history_back = MagicMock() + gramplet._history_forward = MagicMock() + gramplet.completion = MagicMock() + gramplet.completion.on_key_press.return_value = False + return gramplet + + +# ------------------------------------------------------------ +# +# HistoryNavigationTest +# +# ------------------------------------------------------------ +class HistoryNavigationTest(unittest.TestCase): + """``_remember_history``/``_history_back``/``_history_forward`` in + isolation, through the ``_get_expr_text``/``_set_expr_text`` seam. + """ + + def test_remember_history_appends_and_resets_index(self): + gramplet = _make_gramplet() + gramplet.history_index = 0 # pretend we were mid-browse + + gramplet._remember_history("gender == Person.MALE") + + self.assertEqual(gramplet.history, ["gender == Person.MALE"]) + self.assertIsNone(gramplet.history_index) + + def test_remember_history_skips_immediate_repeat(self): + gramplet = _make_gramplet() + gramplet._remember_history("gender == Person.MALE") + gramplet._remember_history("gender == Person.MALE") + + self.assertEqual(gramplet.history, ["gender == Person.MALE"]) + + def test_remember_history_ignores_empty_expression(self): + gramplet = _make_gramplet() + gramplet._remember_history("") + + self.assertEqual(gramplet.history, []) + + def test_history_back_recalls_most_recent_first_and_saves_draft(self): + gramplet = _make_gramplet("draft in progress") + gramplet.history = ["first", "second"] + + gramplet._history_back() + + self.assertEqual(gramplet._get_expr_text(), "second") + self.assertEqual(gramplet.history_draft, "draft in progress") + + def test_history_back_twice_walks_further_back(self): + gramplet = _make_gramplet() + gramplet.history = ["first", "second"] + + gramplet._history_back() + gramplet._history_back() + + self.assertEqual(gramplet._get_expr_text(), "first") + + def test_history_back_stops_at_oldest_entry(self): + gramplet = _make_gramplet() + gramplet.history = ["only"] + + gramplet._history_back() + gramplet._history_back() + + self.assertEqual(gramplet._get_expr_text(), "only") + + def test_history_back_with_empty_history_is_a_no_op(self): + gramplet = _make_gramplet("still typing") + + gramplet._history_back() + + self.assertEqual(gramplet._get_expr_text(), "still typing") + + def test_history_forward_returns_to_draft_past_newest(self): + gramplet = _make_gramplet("draft in progress") + gramplet.history = ["first", "second"] + gramplet._history_back() # -> "second", saves the draft + + gramplet._history_forward() + + self.assertEqual(gramplet._get_expr_text(), "draft in progress") + self.assertIsNone(gramplet.history_index) + + def test_history_forward_without_browsing_is_a_no_op(self): + gramplet = _make_gramplet("still typing") + + gramplet._history_forward() + + self.assertEqual(gramplet._get_expr_text(), "still typing") + + +# ------------------------------------------------------------ +# +# KeyPressTest +# +# ------------------------------------------------------------ +class KeyPressTest(unittest.TestCase): + """``_on_key_press``'s dispatch: completion gets first look at every key + (so its popover's Up/Down/Enter/Escape work); Ctrl+Enter runs Find; + Tab is always consumed -- it never inserts a tab character, even when + there was nothing to complete; Up/Down (when completion didn't + consume them) only recall history at the first/last line of the + buffer, so normal cursor movement inside a multi-line expression + still works. + """ + + def test_ctrl_enter_runs_find_and_is_consumed(self): + gramplet = _make_gramplet_for_keypress() + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Return, ctrl=True)) + + self.assertTrue(handled) + gramplet.find_clicked.assert_called_once() + + def test_plain_enter_is_not_consumed(self): + gramplet = _make_gramplet_for_keypress() + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Return, ctrl=False)) + + self.assertFalse(handled) + gramplet.find_clicked.assert_not_called() + + def test_up_at_first_line_of_single_line_buffer_recalls_history(self): + gramplet = _make_gramplet_for_keypress(cursor_line=0, line_count=1) + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Up)) + + self.assertTrue(handled) + gramplet._history_back.assert_called_once() + + def test_up_on_a_later_line_of_a_multiline_buffer_is_not_consumed(self): + gramplet = _make_gramplet_for_keypress(cursor_line=1, line_count=2) + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Up)) + + self.assertFalse(handled) + gramplet._history_back.assert_not_called() + + def test_down_on_an_earlier_line_of_a_multiline_buffer_is_not_consumed(self): + gramplet = _make_gramplet_for_keypress(cursor_line=0, line_count=2) + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Down)) + + self.assertFalse(handled) + gramplet._history_forward.assert_not_called() + + def test_down_at_last_line_recalls_history(self): + gramplet = _make_gramplet_for_keypress(cursor_line=1, line_count=2) + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Down)) + + self.assertTrue(handled) + gramplet._history_forward.assert_called_once() + + def test_tab_is_consumed_even_with_nothing_to_complete(self): + gramplet = _make_gramplet_for_keypress() + gramplet.completion.on_key_press.return_value = False # nothing completable + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Tab)) + + self.assertTrue(handled) # never falls back to inserting a tab + + def test_tab_delegates_to_completion_first(self): + gramplet = _make_gramplet_for_keypress() + gramplet.completion.on_key_press.return_value = True # triggered/accepted + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Tab)) + + self.assertTrue(handled) + gramplet.completion.on_key_press.assert_called_once() + + def test_completion_open_intercepts_up_before_history_navigation(self): + """When the completion popover is open, Up/Down navigate it, not + history -- CompletionController.on_key_press itself decides this + (returns True while open); this just checks _on_key_press honors + that decision instead of also running its own Up/Down handling. + """ + gramplet = _make_gramplet_for_keypress(cursor_line=0, line_count=1) + gramplet.completion.on_key_press.return_value = True + + handled = gramplet._on_key_press(None, _key_event(Gdk.KEY_Up)) + + self.assertTrue(handled) + gramplet._history_back.assert_not_called() + + +# ------------------------------------------------------------ +# +# HistoryPersistenceTest +# +# ------------------------------------------------------------ +class HistoryPersistenceTest(unittest.TestCase): + """``on_load``/``on_save`` round-trip history through ``self.gui.data`` + -- the same per-instance saved-placement mechanism core gramplets like + ``plugins/gramplet/pedigreegramplet.py`` use for their own persisted + options (``Gramplet.__init__`` calls ``init()`` then ``on_load()``; + ``GrampletBar.on_delete`` calls ``on_save()`` before writing + ``self.gui.data`` to the gramplet's saved-placement .ini file). + """ + + def test_on_load_restores_history_from_gui_data(self): + gramplet = goql_gramplet.PersonQueryFilter.__new__( + goql_gramplet.PersonQueryFilter + ) + gramplet.gui = MagicMock() + gramplet.gui.data = ["first", "second"] + + gramplet.on_load() + + self.assertEqual(gramplet.history, ["first", "second"]) + + def test_on_save_writes_history_to_gui_data(self): + gramplet = _make_gramplet() + gramplet.history = ["first", "second"] + + gramplet.on_save() + + self.assertEqual(gramplet.gui.data, ["first", "second"]) + + def test_remember_history_caps_at_max_entries(self): + gramplet = _make_gramplet() + total = goql_gramplet.HISTORY_MAX_ENTRIES + 5 + for i in range(total): + gramplet._remember_history("expr %d" % i) + + self.assertEqual(len(gramplet.history), goql_gramplet.HISTORY_MAX_ENTRIES) + self.assertEqual(gramplet.history[0], "expr 5") + self.assertEqual(gramplet.history[-1], "expr %d" % (total - 1)) + + +# ------------------------------------------------------------ +# +# FilterProgressTest +# +# ------------------------------------------------------------ +class FilterProgressTest(unittest.TestCase): + """``_run_with_filter_progress``/``_filter_method_label`` -- the + Prepare-time/Apply-time + SQL-vs-eval diagnostics mirrored from + ``gui/filters/sidebar/_sidebarfilter.py``'s ``clicked()``. + ``GenericFilter.apply()`` already reports those phase timings via + ``user.notify(...)``; without ``uistate.filter_print_func`` wired up, + ``gui/user.py``'s ``User._gui_print`` sends them to stdout instead of + any widget -- which is exactly why they only ever showed up in a + terminal before this. + """ + + def test_run_with_filter_progress_wires_and_clears_the_hooks(self): + gramplet = _make_gramplet() + seen = {} + + def action(): + seen["print_func_during"] = gramplet.uistate.filter_print_func + seen["step_func_during"] = gramplet.uistate.filter_step_func + + gramplet._run_with_filter_progress(action) + + self.assertIsNotNone(seen["print_func_during"]) + self.assertIsNotNone(seen["step_func_during"]) + self.assertIsNone(gramplet.uistate.filter_print_func) + self.assertIsNone(gramplet.uistate.filter_step_func) + + def test_run_with_filter_progress_clears_hooks_even_on_exception(self): + gramplet = _make_gramplet() + + def action(): + raise RuntimeError("boom") + + with self.assertRaises(RuntimeError): + gramplet._run_with_filter_progress(action) + + self.assertIsNone(gramplet.uistate.filter_print_func) + self.assertIsNone(gramplet.uistate.filter_step_func) + + def test_run_with_filter_progress_collects_messages(self): + gramplet = _make_gramplet() + + def action(): + gramplet.uistate.filter_print_func("Prepare time: 0.01s") + gramplet.uistate.filter_print_func("Apply time: 0.02s") + + phase_msgs = gramplet._run_with_filter_progress(action) + + self.assertEqual(phase_msgs, ["Prepare time: 0.01s", "Apply time: 0.02s"]) + + def test_filter_method_label_reports_sql_when_a_rule_has_selected_handles(self): + gramplet = _make_gramplet() + rule = MagicMock() + rule.selected_handles = {"h1"} + gfilter = MagicMock() + gfilter.get_rules.return_value = [rule] + + self.assertEqual(gramplet._filter_method_label(gfilter), "SQL") + + def test_filter_method_label_reports_eval_when_no_rule_has_selected_handles(self): + gramplet = _make_gramplet() + rule = MagicMock() + rule.selected_handles = None + gfilter = MagicMock() + gfilter.get_rules.return_value = [rule] + + self.assertEqual(gramplet._filter_method_label(gfilter), "Python evaluation") + + +# ------------------------------------------------------------ +# +# HelpButtonTest +# +# ------------------------------------------------------------ +class HelpButtonTest(unittest.TestCase): + """``self.gui.help_url`` is set from the ``.gpr.py`` registration's + ``help_url=`` -- the same attribute/dispatch the built-in per-gramplet + "Help" menu item already uses (``gui/widgets/grampletpane.py``/ + ``grampletbar.py``). + """ + + def test_help_clicked_opens_wiki_page_for_a_non_url_help_url(self): + gramplet = _make_gramplet() + gramplet.gui.help_url = "Addon:GrampsObjectQueryLanguage" + + with patch.object(goql_gramplet, "display_help") as mock_help, patch.object( + goql_gramplet, "display_url" + ) as mock_url: + gramplet.help_clicked(None) + + mock_help.assert_called_once_with("Addon:GrampsObjectQueryLanguage") + mock_url.assert_not_called() + + def test_help_clicked_opens_a_raw_url_directly(self): + gramplet = _make_gramplet() + gramplet.gui.help_url = "https://example.org/help" + + with patch.object(goql_gramplet, "display_help") as mock_help, patch.object( + goql_gramplet, "display_url" + ) as mock_url: + gramplet.help_clicked(None) + + mock_url.assert_called_once_with("https://example.org/help") + mock_help.assert_not_called() + + def test_help_clicked_with_no_help_url_does_nothing(self): + gramplet = _make_gramplet() + gramplet.gui.help_url = None + + with patch.object(goql_gramplet, "display_help") as mock_help, patch.object( + goql_gramplet, "display_url" + ) as mock_url: + gramplet.help_clicked(None) + + mock_help.assert_not_called() + mock_url.assert_not_called() + + +if __name__ == "__main__": + unittest.main() diff --git a/GOQLFilter/tests/test_goql_completion.py b/GOQLFilter/tests/test_goql_completion.py new file mode 100644 index 000000000..a91a860ee --- /dev/null +++ b/GOQLFilter/tests/test_goql_completion.py @@ -0,0 +1,125 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Tests for ``goql_completion.py``'s completion source -- pure logic, no +GTK, no database, matching the module's own "no display needed" design. + +Run with:: + + python3 -m unittest GOQLFilter.tests.test_goql_completion -v +""" + +import os +import sys +import unittest + +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + from goql_completion import get_completion_items +except ImportError as exc: + raise unittest.SkipTest( + "goql_completion import failed (likely missing " + "gramps-object-query-language): %s" % exc + ) + + +def _names(source, line, column, namespace): + return sorted( + item["name"] for item in get_completion_items(source, line, column, namespace) + ) + + +# ------------------------------------------------------------ +# +# TopLevelCompletionTest +# +# ------------------------------------------------------------ +class TopLevelCompletionTest(unittest.TestCase): + def test_person_top_level_includes_columns_relationships_and_keywords(self): + names = _names("", 1, 0, "Person") + for expected in ("gender", "birth", "death", "families", "and", "or", "not"): + self.assertIn(expected, names) + + def test_person_top_level_includes_constant_class_names(self): + names = _names("", 1, 0, "Person") + self.assertIn("Person", names) + self.assertIn("EventType", names) + + def test_top_level_includes_comparison_operators(self): + """A blank Tab press shows the comparison operators too -- most are + a single character, so they only ever surface with an empty + prefix, but they should still be there to see.""" + names = _names("", 1, 0, "Person") + for op in ("==", "!=", "<", "<=", ">", ">="): + self.assertIn(op, names) + + def test_family_namespace_differs_from_person(self): + family_names = _names("", 1, 0, "Family") + self.assertIn("father", family_names) + self.assertIn("mother", family_names) + self.assertIn("children", family_names) + self.assertNotIn("birth", family_names) + self.assertNotIn("death", family_names) + + def test_top_level_prefix_filters_matches(self): + names = _names("gen", 1, 3, "Person") + self.assertIn("gender", names) + self.assertNotIn("birth", names) + + def test_unknown_namespace_returns_no_completions(self): + names = _names("", 1, 0, "NotARealType") + self.assertEqual(names, []) + + +# ------------------------------------------------------------ +# +# DottedConstantCompletionTest +# +# ------------------------------------------------------------ +class DottedConstantCompletionTest(unittest.TestCase): + def test_person_dot_lists_gender_constants(self): + source = "gender == Person." + names = _names(source, 1, len(source), "Person") + self.assertEqual(names, sorted(["FEMALE", "MALE", "OTHER", "UNKNOWN"])) + + def test_person_dot_prefix_filters_to_matching_constants(self): + source = "gender == Person.MA" + items = get_completion_items(source, 1, len(source), "Person") + self.assertEqual([(i["name"], i["complete"]) for i in items], [("MALE", "LE")]) + + def test_unrecognized_class_name_before_dot_returns_no_completions(self): + source = "primary_name." + names = _names(source, 1, len(source), "Person") + self.assertEqual(names, []) + + def test_dotted_completion_ignores_the_active_namespace(self): + """`Date.MOD_ABOUT` is valid regardless of which namespace + (Person/Family/...) the expression is being written for -- the + constant-class table isn't namespace-scoped.""" + source = "birth.date.modifier == Date." + names = _names(source, 1, len(source), "Family") + self.assertIn("MOD_ABOUT", names) + + +if __name__ == "__main__": + unittest.main() diff --git a/GOQLFilter/tests/test_goql_completion_popup.py b/GOQLFilter/tests/test_goql_completion_popup.py new file mode 100644 index 000000000..a7e5e5497 --- /dev/null +++ b/GOQLFilter/tests/test_goql_completion_popup.py @@ -0,0 +1,215 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Tests for ``goql_completion_popup.CompletionController``'s decision +logic, driven with a mocked ``Gtk.TextView``/``Gtk.TextBuffer`` rather than +real ones -- ``Gtk.TextView()`` needs an actual display connection just to +construct (see ``test_goql.py``'s ``KeyPressTest`` fixtures for the same +constraint under this repo's documented ``GDK_BACKEND=-`` test invocation). +Real-popover paths (multi-match) aren't covered here for the same reason; +``_compute_items`` is patched directly to isolate ``trigger()``'s own +single/zero-match branching from how items get computed. + +Run with:: + + python3 -m unittest GOQLFilter.tests.test_goql_completion_popup -v +""" + +import os +import sys +import types +import unittest +from unittest.mock import MagicMock + +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + import gi # noqa: F401 + from gi.repository import Gdk +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) + +try: + from goql_completion_popup import CompletionController +except ImportError as exc: + raise unittest.SkipTest( + "goql_completion_popup import failed (likely missing " + "gramps-object-query-language): %s" % exc + ) + + +def _make_controller(): + textview = MagicMock() + buffer = MagicMock() + textview.get_buffer.return_value = buffer + controller = CompletionController(textview, get_namespace=lambda: "Person") + return controller, buffer + + +def _key_event(keyval): + return types.SimpleNamespace(keyval=keyval, state=Gdk.ModifierType(0)) + + +# ------------------------------------------------------------ +# +# TriggerTest +# +# ------------------------------------------------------------ +class TriggerTest(unittest.TestCase): + def test_single_match_inserts_directly_without_opening_a_popover(self): + controller, buffer = _make_controller() + controller._compute_items = MagicMock( + return_value=[{"name": "MALE", "complete": "LE", "cursor_offset": 0}] + ) + + handled = controller.trigger() + + self.assertTrue(handled) + self.assertFalse(controller.is_open()) + buffer.insert.assert_called_once() + self.assertEqual(buffer.insert.call_args.args[1], "LE") + + def test_no_matches_is_a_no_op(self): + controller, buffer = _make_controller() + controller._compute_items = MagicMock(return_value=[]) + + handled = controller.trigger() + + self.assertFalse(handled) + self.assertFalse(controller.is_open()) + buffer.insert.assert_not_called() + + +# ------------------------------------------------------------ +# +# AcceptCloseTest +# +# ------------------------------------------------------------ +class AcceptCloseTest(unittest.TestCase): + def test_accept_inserts_the_selected_items_complete_text(self): + controller, buffer = _make_controller() + controller.items = [ + {"name": "count(...)", "complete": "count()", "cursor_offset": 0} + ] + controller.selected_index = 0 + + controller.accept() + + buffer.insert.assert_called_once() + self.assertEqual(buffer.insert.call_args.args[1], "count()") + + def test_accept_moves_cursor_back_by_cursor_offset(self): + controller, buffer = _make_controller() + controller.items = [ + {"name": "count(...)", "complete": "count()", "cursor_offset": 1} + ] + controller.selected_index = 0 + cursor_iter = MagicMock() + buffer.get_iter_at_mark.return_value = cursor_iter + + controller.accept() + + cursor_iter.backward_chars.assert_called_once_with(1) + buffer.place_cursor.assert_called_once_with(cursor_iter) + + def test_close_resets_state(self): + controller, _buffer = _make_controller() + controller.items = [{"name": "x", "complete": "", "cursor_offset": 0}] + controller.selected_index = 2 + controller.popover = MagicMock() + + controller.close() + + self.assertIsNone(controller.popover) + self.assertEqual(controller.items, []) + self.assertEqual(controller.selected_index, 0) + + +# ------------------------------------------------------------ +# +# OnKeyPressTest +# +# ------------------------------------------------------------ +class OnKeyPressTest(unittest.TestCase): + """Mirrors GOQLFilter/goql.py's own reliance on this dispatch: Tab + triggers when closed and accepts when open; Up/Down/Enter/Escape only + do anything while the popover is open, so a closed controller leaves + them for the host widget (goql.py's own history navigation) to handle. + """ + + def test_tab_triggers_when_closed(self): + controller, _buffer = _make_controller() + controller.trigger = MagicMock(return_value=True) + + handled = controller.on_key_press(_key_event(Gdk.KEY_Tab)) + + self.assertTrue(handled) + controller.trigger.assert_called_once() + + def test_tab_accepts_when_open(self): + controller, _buffer = _make_controller() + controller.popover = MagicMock() # is_open() -> True + controller.accept = MagicMock() + + handled = controller.on_key_press(_key_event(Gdk.KEY_Tab)) + + self.assertTrue(handled) + controller.accept.assert_called_once() + + def test_escape_closes_when_open(self): + controller, _buffer = _make_controller() + controller.popover = MagicMock() + controller.close = MagicMock() + + handled = controller.on_key_press(_key_event(Gdk.KEY_Escape)) + + self.assertTrue(handled) + controller.close.assert_called_once() + + def test_up_is_not_consumed_when_closed(self): + controller, _buffer = _make_controller() + + handled = controller.on_key_press(_key_event(Gdk.KEY_Up)) + + self.assertFalse(handled) + + def test_up_moves_selection_when_open(self): + controller, _buffer = _make_controller() + controller.popover = MagicMock() + controller.move_selection = MagicMock() + + handled = controller.on_key_press(_key_event(Gdk.KEY_Up)) + + self.assertTrue(handled) + controller.move_selection.assert_called_once_with(-1) + + def test_never_propagates_an_exception(self): + controller, _buffer = _make_controller() + controller.trigger = MagicMock(side_effect=RuntimeError("boom")) + + handled = controller.on_key_press(_key_event(Gdk.KEY_Tab)) + + self.assertFalse(handled) + + +if __name__ == "__main__": + unittest.main() diff --git a/GOQLFilter/tests/test_goql_highlight.py b/GOQLFilter/tests/test_goql_highlight.py new file mode 100644 index 000000000..dcfddfcdc --- /dev/null +++ b/GOQLFilter/tests/test_goql_highlight.py @@ -0,0 +1,110 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Tests for ``goql_highlight.py``'s token classification -- pure logic, no +GTK, no database. + +Run with:: + + python3 -m unittest GOQLFilter.tests.test_goql_highlight -v +""" + +import os +import sys +import unittest + +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + from goql_highlight import classify_tokens +except ImportError as exc: + raise unittest.SkipTest( + "goql_highlight import failed (likely missing " + "gramps-object-query-language): %s" % exc + ) + + +def _spans_by_text(source): + """(category, matched substring) pairs, in source order -- easier to + assert against than raw line/column spans.""" + lines = source.splitlines() + result = [] + for start_line, start_col, end_line, end_col, category in classify_tokens(source): + text = ( + lines[start_line][start_col:end_col] + if start_line == end_line + else "" + ) + result.append((category, text)) + return result + + +# ------------------------------------------------------------ +# +# ClassifyTokensTest +# +# ------------------------------------------------------------ +class ClassifyTokensTest(unittest.TestCase): + def test_classifies_operator_constant_class_keyword_string_and_number(self): + source = "gender == Person.MALE and 'Anderson' in primary_name.surname_list[0].surname" + spans = _spans_by_text(source) + self.assertIn(("operator", "=="), spans) + self.assertIn(("constant-class", "Person"), spans) + self.assertIn(("keyword", "and"), spans) + self.assertIn(("string", "'Anderson'"), spans) + self.assertIn(("keyword", "in"), spans) + self.assertIn(("number", "0"), spans) + + def test_plain_field_names_are_not_classified(self): + spans = _spans_by_text("gender == Person.MALE") + categories_by_text = dict((text, category) for category, text in spans) + self.assertNotIn("gender", categories_by_text) + + def test_incomplete_expression_still_yields_tokens_before_the_break(self): + """An unterminated string is the normal state of this buffer mid-typing, + not an error to surface -- whatever tokenized cleanly before it is + still returned.""" + spans = _spans_by_text("gender == Person.MALE and 'Anders") + self.assertIn(("operator", "=="), spans) + self.assertIn(("constant-class", "Person"), spans) + self.assertIn(("keyword", "and"), spans) + + def test_empty_source_yields_no_spans(self): + self.assertEqual(list(classify_tokens("")), []) + + def test_like_date_exists_count_are_keywords(self): + spans = _spans_by_text( + "like(x, 'A%') and exists(children) and count(events) > 1" + ) + categories_by_text = dict((text, category) for category, text in spans) + self.assertEqual(categories_by_text.get("like"), "keyword") + self.assertEqual(categories_by_text.get("exists"), "keyword") + self.assertEqual(categories_by_text.get("count"), "keyword") + + def test_comparison_operators_are_all_recognized(self): + for op in ("==", "!=", "<", "<=", ">", ">="): + spans = _spans_by_text("gender %s 1" % op) + self.assertIn(("operator", op), spans, "missing operator %r" % op) + + +if __name__ == "__main__": + unittest.main() diff --git a/GOQLFilter/tests/test_integration_whereexprrule.py b/GOQLFilter/tests/test_integration_whereexprrule.py new file mode 100644 index 000000000..50a4e37a7 --- /dev/null +++ b/GOQLFilter/tests/test_integration_whereexprrule.py @@ -0,0 +1,264 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Integration tests for ``whereexprrule.py``. + +Drives the rule the same way ``GenericFilter.apply()`` does -- through a +real ``GenericFilter``/``CacheProxyDb`` against a real temp SQLite db, since +that composition (Rule -> GenericFilter -> what a gramplet hands to +``view.generic_filter``) is the thing actually being tested, not the GOQL +compiler in isolation (already covered by gramps-object-query-language's +own test suite). DB-backed, so this is Linux-only in CI (``test_integration_`` +prefix) rather than the plain ``test_`` prefix. + +Run with:: + + python3 -m unittest GOQLFilter.tests.test_integration_whereexprrule -v +""" + +import os +import shutil +import sys +import tempfile +import unittest + +ADDON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if ADDON_DIR not in sys.path: + sys.path.insert(0, ADDON_DIR) + +try: + import gi +except ImportError as err: + raise unittest.SkipTest("PyGObject not available: %s" % err) + +try: + from whereexprrule import ( + FamilyMatchesExpression, + PersonMatchesExpression, + _resolve_dialect, + _unwrap_cache_proxy, + ) +except ImportError as exc: + raise unittest.SkipTest( + "whereexprrule import failed (likely missing " + "gramps-object-query-language): %s" % exc + ) + +from gramps.gen.db import DbTxn +from gramps.gen.db.utils import make_database +from gramps.gen.filters import GenericFilterFactory +from gramps.gen.lib import Family, Name, Person, Surname +from gramps.gen.proxy import CacheProxyDb, PrivateProxyDb +from gramps_object_query_language.query import Dialect + + +def _name(given, surname): + name = Name() + name.set_first_name(given) + surn = Surname() + surn.set_surname(surname) + name.set_surname_list([surn]) + return name + + +# ------------------------------------------------------------ +# +# WhereExprRuleTest +# +# ------------------------------------------------------------ +class WhereExprRuleTest(unittest.TestCase): + def setUp(self): + self.tmp_dir = tempfile.mkdtemp(prefix="goql_addon_") + self.db = make_database("sqlite") + self.db.load(self.tmp_dir) + + self.handles = {} + with DbTxn("build test db", self.db) as trans: + father = Person() + father.set_primary_name(_name("Karl", "Anderson")) + father.set_gender(Person.MALE) + self.handles["father"] = self.db.add_person(father, trans) + + mother = Person() + mother.set_primary_name(_name("Lena", "Baker")) + mother.set_gender(Person.FEMALE) + self.handles["mother"] = self.db.add_person(mother, trans) + + son = Person() + son.set_primary_name(_name("Otto", "Anderson")) + son.set_gender(Person.MALE) + self.handles["son"] = self.db.add_person(son, trans) + + family = Family() + family.set_father_handle(self.handles["father"]) + family.set_mother_handle(self.handles["mother"]) + self.handles["family"] = self.db.add_family(family, trans) + + def tearDown(self): + self.db.close() + shutil.rmtree(self.tmp_dir, ignore_errors=True) + + def _apply_person_filter(self, expr): + gfilter = GenericFilterFactory("Person")() + gfilter.add_rule(PersonMatchesExpression([expr])) + cdb = CacheProxyDb(self.db) + return set(gfilter.apply(cdb, self.db.get_person_handles())) + + def test_flat_column_match(self): + matched = self._apply_person_filter("gender == Person.MALE") + self.assertEqual(matched, {self.handles["father"], self.handles["son"]}) + + def test_json_path_and_boolean_combination(self): + matched = self._apply_person_filter( + "gender == Person.MALE and " + "'Anderson' in primary_name.surname_list[0].surname" + ) + self.assertEqual(matched, {self.handles["father"], self.handles["son"]}) + + def test_no_match(self): + matched = self._apply_person_filter("gender == Person.UNKNOWN") + self.assertEqual(matched, set()) + + def test_empty_expression_matches_nothing(self): + matched = self._apply_person_filter("") + self.assertEqual(matched, set()) + + def test_invalid_expression_matches_nothing_rather_than_raising(self): + matched = self._apply_person_filter("this is not valid GOQL !!") + self.assertEqual(matched, set()) + + def test_family_related_object_path(self): + gfilter = GenericFilterFactory("Family")() + gfilter.add_rule(FamilyMatchesExpression(["father.surname == 'Anderson'"])) + cdb = CacheProxyDb(self.db) + matched = set(gfilter.apply(cdb, self.db.get_family_handles())) + self.assertEqual(matched, {self.handles["family"]}) + + +# ------------------------------------------------------------ +# +# SqlPushDownTest +# +# ------------------------------------------------------------ +class SqlPushDownTest(unittest.TestCase): + """Regression coverage for two bugs found wiring up SQL push-down: + + 1. ``_resolve_dialect`` used to do ``isinstance(db, SQLite)``, but + Gramps' plugin loader imports ``sqlite.py`` as a bare top-level + module named ``sqlite``, not via ``gramps.plugins.db.dbapi.sqlite`` + -- so a live ``dbstate.db``'s class is never ``isinstance``- + compatible with a normally-imported ``SQLite``, and dialect + resolution silently fell through every time. Fixed by matching + ``type(db).__name__`` instead of ``isinstance``. + 2. Every real filter application wraps ``db`` in ``CacheProxyDb`` + (``gui/views/treemodels/flatbasemodel.py``'s ``_rebuild_filter``: + ``cdb = CacheProxyDb(self.db); self.search.apply(cdb, ...)``), which + is not a ``ProxyDbBase`` subclass and forwards attribute access via + ``__getattr__``. Left unpeeled, that defeats the + ``isinstance(db, ProxyDbBase)`` privacy check whenever a real + privacy proxy is nested *underneath* it + (``CacheProxyDb(PrivateProxyDb(db))``) -- SQL would run straight + past privacy filtering. Fixed with ``_unwrap_cache_proxy``. + """ + + def setUp(self): + self.tmp_dir = tempfile.mkdtemp(prefix="goql_sql_pushdown_") + self.db = make_database("sqlite") + self.db.load(self.tmp_dir) + with DbTxn("build test db", self.db) as trans: + father = Person() + father.set_primary_name(_name("Karl", "Anderson")) + father.set_gender(Person.MALE) + self.father_handle = self.db.add_person(father, trans) + + mother = Person() + mother.set_primary_name(_name("Lena", "Baker")) + mother.set_gender(Person.FEMALE) + self.mother_handle = self.db.add_person(mother, trans) + + def tearDown(self): + self.db.close() + shutil.rmtree(self.tmp_dir, ignore_errors=True) + + def test_resolve_dialect_matches_live_sqlite_db_by_class_name(self): + self.assertEqual(_resolve_dialect(self.db), Dialect.SQLITE) + + def test_resolve_dialect_is_none_for_an_unrecognized_backend(self): + self.assertIsNone(_resolve_dialect(object())) + + def test_unwrap_cache_proxy_peels_to_the_raw_db(self): + cdb = CacheProxyDb(self.db) + self.assertIs(_unwrap_cache_proxy(cdb), self.db) + + def test_unwrap_cache_proxy_stops_at_a_nested_privacy_proxy(self): + pdb = PrivateProxyDb(self.db) + cdb = CacheProxyDb(pdb) + self.assertIs(_unwrap_cache_proxy(cdb), pdb) + + def test_sql_push_down_engages_through_the_cache_proxy_wrapping(self): + """The exact wrapping every real GenericFilter.apply() call uses.""" + cdb = CacheProxyDb(self.db) + rule = PersonMatchesExpression(["gender == Person.MALE"]) + + rule.prepare(cdb, None) + + self.assertEqual(rule.selected_handles, {self.father_handle}) + + def test_privacy_proxy_nested_under_cache_proxy_disables_sql_push_down(self): + cdb = CacheProxyDb(PrivateProxyDb(self.db)) + rule = PersonMatchesExpression(["gender == Person.MALE"]) + + rule.prepare(cdb, None) + + self.assertIsNone(rule.selected_handles) + # Still correct via the per-object eval fallback: + father = cdb.get_person_from_handle(self.father_handle) + mother = cdb.get_person_from_handle(self.mother_handle) + self.assertTrue(rule.apply_to_one(cdb, father)) + self.assertFalse(rule.apply_to_one(cdb, mother)) + + def test_optimizer_recognizes_selected_handles(self): + """Regression test for the actual ~8s "Apply time" bug: the + precomputed match set used to be stored as ``_matched_handles``, + a name ``gen.filters.optimizer.Optimizer`` doesn't look for. + ``compute_potential_handles_for_rule`` only checks + ``hasattr(rule, "selected_handles")`` -- anything else is + invisible to it, so ``GenericFilter.apply()`` fetched and + deserialized every candidate via ``get_object()`` before + ``apply_to_one`` ever ran, no matter how fast ``apply_to_one`` + itself was. Renaming the attribute to what the Optimizer actually + checks for is the fix; this asserts that contract directly rather + than just re-checking ``apply_to_one``'s own behavior. + """ + from gramps.gen.filters.optimizer import Optimizer + + cdb = CacheProxyDb(self.db) + rule = PersonMatchesExpression(["gender == Person.MALE"]) + rule.prepare(cdb, None) + + optimizer = Optimizer(GenericFilterFactory("Person")()) + handles_in, handles_out = optimizer.compute_potential_handles_for_rule(rule) + + self.assertEqual(handles_in, {self.father_handle}) + self.assertIsNone(handles_out) + + +if __name__ == "__main__": + unittest.main() diff --git a/GOQLFilter/whereexprrule.gpr.py b/GOQLFilter/whereexprrule.gpr.py new file mode 100644 index 000000000..543f7a6d5 --- /dev/null +++ b/GOQLFilter/whereexprrule.gpr.py @@ -0,0 +1,205 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Filter rules matching objects against a GOQL where-expression.""" + +_HELP = "Addon:GrampsObjectQueryLanguage" +_AUTHORS = ["Douglas Blank"] +_AUTHORS_EMAIL = ["doug.blank@gmail.com"] + +register( + RULE, + id="PersonMatchesExpression", + name=_("People matching the "), + description=_( + "Matches people for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="PersonMatchesExpression", # must be rule class name + namespace="Person", # one of the primary object classes + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="FamilyMatchesExpression", + name=_("Families matching the "), + description=_( + "Matches families for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="FamilyMatchesExpression", + namespace="Family", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="EventMatchesExpression", + name=_("Events matching the "), + description=_( + "Matches events for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="EventMatchesExpression", + namespace="Event", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="PlaceMatchesExpression", + name=_("Places matching the "), + description=_( + "Matches places for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="PlaceMatchesExpression", + namespace="Place", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="RepositoryMatchesExpression", + name=_("Repositories matching the "), + description=_( + "Matches repositories for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="RepositoryMatchesExpression", + namespace="Repository", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="SourceMatchesExpression", + name=_("Sources matching the "), + description=_( + "Matches sources for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="SourceMatchesExpression", + namespace="Source", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="CitationMatchesExpression", + name=_("Citations matching the "), + description=_( + "Matches citations for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="CitationMatchesExpression", + namespace="Citation", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="MediaMatchesExpression", + name=_("Media matching the "), + description=_( + "Matches media for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="MediaMatchesExpression", + namespace="Media", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) + +register( + RULE, + id="NoteMatchesExpression", + name=_("Notes matching the "), + description=_( + "Matches notes for which the given gramps-object-query-language " + "where-expression evaluates to true" + ), + version="1.0.0", + gramps_target_version="6.1", + status=STABLE, + fname="whereexprrule.py", + ruleclass="NoteMatchesExpression", + namespace="Note", + authors=_AUTHORS, + authors_email=_AUTHORS_EMAIL, + help_url=_HELP, + requires_mod=["gramps_object_query_language"], +) diff --git a/GOQLFilter/whereexprrule.py b/GOQLFilter/whereexprrule.py new file mode 100644 index 000000000..50b048adb --- /dev/null +++ b/GOQLFilter/whereexprrule.py @@ -0,0 +1,302 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2026 Douglas Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"""Filter rule matching objects against a gramps-object-query-language +where-expression, one subclass per primary object type. + +Registering these as ordinary ``RULE`` plugins (see ``whereexprrule.gpr.py``) +lets a GOQL expression be dropped into Gramps' own Custom Filter Editor +(``EditFilter``) as a single rule, so a filter built from one now shows up +everywhere a normal Custom Filter would -- other views' sidebar filters, +reports, exports -- not just in this addon's own gramplet. + +When ``prepare()`` sees an unproxied, DB-API-backed ``db``, it pushes the +expression down to SQL (``gramps_object_query_language.query.compile_query``) +instead of evaluating it per-object -- the same fast/slow split +gramps-web-api's ``resources/object_query.py`` makes (``_post_sql`` vs. +``_post_proxied``), just reached from ``Rule.prepare(db, user)`` instead of +a request handler. Re-decided on every ``prepare()`` call, never cached on +the rule instance: a saved Custom Filter can be applied against a different +db, or the same db behind a different proxy, at any later time, and each +application must see its own, current ``db``. +""" + +# ------------------------------------------------------------------------- +# +# Standard Python modules +# +# ------------------------------------------------------------------------- +import logging +from typing import Any, Optional + +# ------------------------------------------------------------------------- +# +# Gramps modules +# +# ------------------------------------------------------------------------- +from gramps.gen.filters.rules import Rule +from gramps.gen.const import GRAMPS_LOCALE as glocale +from gramps.gen.proxy import CacheProxyDb +from gramps.gen.proxy.proxybase import ProxyDbBase + +# ------------------------------------------------------------------------- +# +# gramps-object-query-language modules +# +# ------------------------------------------------------------------------- +try: + from gramps_object_query_language.query_lang import ( + QueryLangError, + compile_expr_for_spec, + ) + from gramps_object_query_language.evaluator import evaluate_where + from gramps_object_query_language.query import ( + CITATION, + EVENT, + FAMILY, + MEDIA, + NOTE, + PERSON, + PLACE, + REPOSITORY, + SOURCE, + Dialect, + Query, + compile_query, + ) +except ImportError as err: + raise ImportError( + "GOQLFilter requires the gramps-object-query-language " + "package.\nInstall with: pip install gramps-object-query-language" + ) from err + +try: + _trans = glocale.get_addon_translator(__file__) +except ValueError: + _trans = glocale.translation +_ = _trans.gettext + +LOG = logging.getLogger(".GOQLFilter.whereexprrule") + +# Adapted from gramps-web-api's resources/object_query.py (_DIALECT_BY_NAME / +# _resolve_dialect) -- core DBAPI/SQLite and the SharedPostgreSQL addon +# don't advertise a `.dialect` attribute yet (proposed but unmerged +# core-side: gramps-project/gramps#2178); the single-user PostgreSQL addon +# already does (`dialect = "postgresql"`), so that's read straight off `db` +# when present. Once core grows a real `.dialect` property, this whole +# function collapses to `getattr(db, "dialect", None)` and this copy can be +# deleted. +# +# Diverges from gramps-web-api's version in one deliberate way: that one +# falls back to `isinstance(basedb, SQLite)` for the common case, but a live +# `dbstate.db` here was constructed through Gramps' *plugin* loader, which +# imports `sqlite.py` as a bare top-level module named `sqlite` -- not via +# `gramps.plugins.db.dbapi.sqlite`, the normal package path this file (and +# gramps-web-api) imports `SQLite` through. That makes `type(db)` a +# *different* class object from the `SQLite` imported here even for a real +# SQLite-backed db (confirmed live: `type(make_database("sqlite")).__module__ +# == "sqlite"`, not `"gramps.plugins.db.dbapi.sqlite"`) -- `isinstance` never +# matches, and the dialect check would fall through silently. Matching by +# class *name* instead sidesteps the module-identity split. Unrecognized +# class names return `None` (never a guessed dialect) so an unknown backend +# just skips SQL push-down rather than risking wrong-dialect SQL -- the +# caller falls back to per-object eval either way. +_DIALECT_BY_NAME = { + "sqlite": Dialect.SQLITE, + "postgres": Dialect.POSTGRESQL, + "postgresql": Dialect.POSTGRESQL, +} +_DIALECT_BY_CLASS_NAME = { + "SQLite": Dialect.SQLITE, + "PostgreSQL": Dialect.POSTGRESQL, + "SharedPostgreSQL": Dialect.POSTGRESQL, +} + + +def _resolve_dialect(db: Any) -> Optional[Dialect]: + name: Optional[str] = getattr(db, "dialect", None) + if name: + dialect = _DIALECT_BY_NAME.get(name) + if dialect is not None: + return dialect + return _DIALECT_BY_CLASS_NAME.get(type(db).__name__) + + +def _unwrap_cache_proxy(db: Any) -> Any: + """Peel off ``CacheProxyDb`` wrapping to find what SQL-eligibility + checks should actually look at. + + Every real call into a ``Rule`` from Gramps' own filtering goes through + ``CacheProxyDb`` -- ``gui/views/treemodels/flatbasemodel.py``'s + ``_rebuild_filter`` unconditionally does ``cdb = CacheProxyDb(self.db); + self.search.apply(cdb, ...)`` -- so ``db`` here is *never* the raw + backend object, even with no privacy proxy in play. ``CacheProxyDb`` + has no privacy relevance (it exists purely to cache fetched objects) + but, unlike ``PrivateProxyDb``/``LivingProxyDb``/etc., it doesn't + subclass ``ProxyDbBase`` -- so both ``isinstance(db, ProxyDbBase)`` and + ``_resolve_dialect(db)``'s class-name lookup silently see straight past + a real backend/proxy underneath it without this unwrap: the former + would miss a nested privacy proxy entirely (treating it as + "unproxied"), the latter would miss the real backend's class name + entirely (``type(CacheProxyDb(...)).__name__ == "CacheProxyDb"``, never + a recognized dialect). + + Stops the moment the current layer is anything other than + ``CacheProxyDb`` -- including a real ``ProxyDbBase`` -- so a privacy + proxy nested underneath (``CacheProxyDb(PrivateProxyDb(raw_db))``) is + correctly left in place for the caller's ``isinstance(_, ProxyDbBase)`` + check to catch, not unwrapped past. + """ + while isinstance(db, CacheProxyDb): + db = db.db + return db + + +def _sql_matched_handles(db: Any, spec: Any, where: Any): + """Matching handles via SQL push-down, or ``None`` if not possible. + + Only ever called with a ``db`` already confirmed unproxied and + DB-API-backed (see ``MatchesExpression.prepare``) -- mirrors + gramps-web-api's ``_post_sql`` dispatch, minus the privacy predicate, + which is exactly why an unproxied `db` is required before this runs at + all (see ``query.py``'s ``compile_query`` docstring: "carries no + privacy predicate"). Never raises: an unrecognized backend (no + resolvable dialect) is a routine, silent "skip the optimization"; + a genuine compile/execute failure is logged, then also falls back to + per-object evaluation rather than breaking the filter outright. + """ + dialect = _resolve_dialect(db) + if dialect is None: + return None + try: + query = Query(select=["handle"], where=where, limit=None) + sql, params = compile_query(spec, query, dialect=dialect) + db.dbapi.execute(sql, params) + return {row[0] for row in db.dbapi.fetchall()} + except Exception: + LOG.exception("GOQL SQL push-down failed; falling back to per-object eval") + return None + + +# ------------------------------------------------------------------------- +# +# MatchesExpression +# +# ------------------------------------------------------------------------- +class MatchesExpression(Rule): + """Base rule: true for objects a GOQL where-expression evaluates true for. + + Not registered directly -- each object type needs its own registered + subclass (below) since a ``RULE`` plugin's ``namespace``/``ruleclass`` + pair, and the ``ObjectTypeSpec`` GOQL needs to compile the expression + against, are fixed per type. + """ + + labels = [_("GOQL expression")] + description = _( + "Matches objects for which the given gramps-object-query-language " + "where-expression evaluates to true" + ) + category = _("General filters") + spec: Any = None # set by each subclass below + + def prepare(self, db, user): + self._where = None + # NOT a private name: `gen.filters.optimizer.Optimizer` specifically + # looks for `selected_handles` (`hasattr(rule, "selected_handles")`) + # to narrow `possible_handles` in `GenericFilter.apply()` *before* + # any `get_object()` fetch -- see `apply_logical_op_to_all`. A rule + # holding its precomputed match set under any other name is + # invisible to the Optimizer: every candidate still gets fetched + # and deserialized before `apply_to_one` ever runs, no matter how + # fast `apply_to_one` itself is. This was set as `_matched_handles` + # originally and measured ~8s "Apply time" on a real tree as a + # result -- renaming it to the name the Optimizer actually checks + # for is the fix, not a cosmetic one. + self.selected_handles = None + expr = (self.list[0] or "").strip() + if not expr: + return + try: + self._where = compile_expr_for_spec(self.spec, expr) + except QueryLangError: + # Leave self._where as None -- an invalid/incomplete expression + # matches nothing rather than raising out of GenericFilter.apply(). + self._where = None + return + # SQL push-down only when `db`, past any CacheProxyDb wrapping, is + # unproxied (no privacy predicate to lose) and DB-API-backed + # (something to push down to). Re-checked here, every call -- see + # this module's docstring. + basedb = _unwrap_cache_proxy(db) + if not isinstance(basedb, ProxyDbBase) and hasattr(basedb, "dbapi"): + self.selected_handles = _sql_matched_handles(basedb, self.spec, self._where) + + def apply_to_one(self, db, obj) -> bool: + if self._where is None or obj is None: + return False + if self.selected_handles is not None: + return obj.handle in self.selected_handles + return evaluate_where(db, obj, self._where, self.spec) + + +class PersonMatchesExpression(MatchesExpression): + name = _("People matching the ") + spec = PERSON + + +class FamilyMatchesExpression(MatchesExpression): + name = _("Families matching the ") + spec = FAMILY + + +class EventMatchesExpression(MatchesExpression): + name = _("Events matching the ") + spec = EVENT + + +class PlaceMatchesExpression(MatchesExpression): + name = _("Places matching the ") + spec = PLACE + + +class RepositoryMatchesExpression(MatchesExpression): + name = _("Repositories matching the ") + spec = REPOSITORY + + +class SourceMatchesExpression(MatchesExpression): + name = _("Sources matching the ") + spec = SOURCE + + +class CitationMatchesExpression(MatchesExpression): + name = _("Citations matching the ") + spec = CITATION + + +class MediaMatchesExpression(MatchesExpression): + name = _("Media matching the ") + spec = MEDIA + + +class NoteMatchesExpression(MatchesExpression): + name = _("Notes matching the ") + spec = NOTE From 2cbc30f8c643162c7a94d211edba912afb272be3 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 2 Aug 2026 15:38:23 -0700 Subject: [PATCH 087/156] Add 'Namespace filter' label --- GOQLFilter/goql.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/GOQLFilter/goql.py b/GOQLFilter/goql.py index d9d8aa59f..549a9e18a 100644 --- a/GOQLFilter/goql.py +++ b/GOQLFilter/goql.py @@ -56,7 +56,7 @@ # GTK/Gnome modules # # ------------------------------------------------------------------------- -from gi.repository import Gdk, Gtk +from gi.repository import Gdk, GLib, Gtk # ------------------------------------------------------------------------- # @@ -195,6 +195,18 @@ def init(self): scroller.set_min_content_height(TEXT_AREA_HEIGHT) scroller.add(self.text_view) + # Reminds the user this expression's fields are specific to this + # view -- e.g. "gender" compiles here but not in the Family filter. + namespace_label = Gtk.Label() + namespace_label.set_markup( + "%s" % GLib.markup_escape_text(_("%s filter") % _(self.NAMESPACE)) + ) + namespace_label.set_xalign(0) + + editor_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=3) + editor_box.pack_start(namespace_label, False, False, 0) + editor_box.pack_start(scroller, True, True, 0) + # Same button shapes/icons as the built-in rule-based sidebar filter # (`gui/filters/sidebar/_sidebarfilter.py`'s `_init_interface`): a # plain mnemonic "Find" button, an icon+label "Reset", plain-text @@ -251,7 +263,7 @@ def init(self): # is also user-draggable afterward, which a fixed split wouldn't be. self.paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) self.paned.set_border_width(6) - self.paned.pack1(scroller, True, True) + self.paned.pack1(editor_box, True, True) self.paned.pack2(bottom_box, False, True) self._paned_position_set = False self.paned.connect("size-allocate", self._init_paned_position) From cd6fe48bcdd3dd531af4640894ae935baf849e3b Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 3 Aug 2026 09:12:19 -0700 Subject: [PATCH 088/156] GOQLFilter: suppress noisy PyGIDeprecationWarning on GLib import Newer PyGObject/GLib builds warn on import (not use) of GLib because unix_signal_add_full() moved to GLibUnix in GLib >= 2.88. This addon never calls that function; scope the filter to just this import so other warnings aren't masked. Reported by Gary during integration testing on a gtk-osx build. --- GOQLFilter/goql.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/GOQLFilter/goql.py b/GOQLFilter/goql.py index 549a9e18a..db0b37000 100644 --- a/GOQLFilter/goql.py +++ b/GOQLFilter/goql.py @@ -56,7 +56,18 @@ # GTK/Gnome modules # # ------------------------------------------------------------------------- -from gi.repository import Gdk, GLib, Gtk +import warnings + +with warnings.catch_warnings(): + # PyGObject warns on *import* of GLib -- not on use -- when the + # underlying GLib build has moved unix_signal_add_full() to GLibUnix + # (GLib >= 2.88). This addon never calls that function; the warning is + # just noise from gi's override machinery. See + # https://gitlab.gnome.org/GNOME/pygobject/-/work_items/757 + warnings.filterwarnings( + "ignore", message=".*unix_signal_add_full.*", category=DeprecationWarning + ) + from gi.repository import Gdk, GLib, Gtk # ------------------------------------------------------------------------- # From 9b828a6e9c5a49ecfaa09c2e375bb3be29937df2 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Tue, 4 Aug 2026 08:54:54 -0700 Subject: [PATCH 089/156] Merge Add GOQLFilter addon- #1007 Includes generation of po/template.pot file --- GOQLFilter/goql.gpr.py | 18 +- GOQLFilter/po/template.pot | 287 ++++++++++++++++++++++++++++++++ GOQLFilter/whereexprrule.gpr.py | 18 +- 3 files changed, 305 insertions(+), 18 deletions(-) create mode 100644 GOQLFilter/po/template.pot diff --git a/GOQLFilter/goql.gpr.py b/GOQLFilter/goql.gpr.py index 39475f4c8..fe194c964 100644 --- a/GOQLFilter/goql.gpr.py +++ b/GOQLFilter/goql.gpr.py @@ -30,7 +30,7 @@ id="Person GOQL Filter", name=_("Person GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language person filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -49,7 +49,7 @@ id="Family GOQL Filter", name=_("Family GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language family filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -68,7 +68,7 @@ id="Event GOQL Filter", name=_("Event GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language event filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -87,7 +87,7 @@ id="Place GOQL Filter", name=_("Place GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language place filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -108,7 +108,7 @@ description=_( "Gramplet providing a gramps-object-query-language repository filter" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -127,7 +127,7 @@ id="Source GOQL Filter", name=_("Source GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language source filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -146,7 +146,7 @@ id="Citation GOQL Filter", name=_("Citation GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language citation filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -165,7 +165,7 @@ id="Media GOQL Filter", name=_("Media GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language media filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", @@ -184,7 +184,7 @@ id="Note GOQL Filter", name=_("Note GOQL Filter"), description=_("Gramplet providing a gramps-object-query-language note filter"), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="goql.py", diff --git a/GOQLFilter/po/template.pot b/GOQLFilter/po/template.pot new file mode 100644 index 000000000..2cb6bea87 --- /dev/null +++ b/GOQLFilter/po/template.pot @@ -0,0 +1,287 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-08-04 08:52-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:217 +msgid "General filters" +msgstr "" + +#: GOQLFilter/whereexprrule.py:261 GOQLFilter/whereexprrule.gpr.py:30 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:266 GOQLFilter/whereexprrule.gpr.py:50 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:271 GOQLFilter/whereexprrule.gpr.py:70 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:276 GOQLFilter/whereexprrule.gpr.py:90 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:281 GOQLFilter/whereexprrule.gpr.py:110 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:286 GOQLFilter/whereexprrule.gpr.py:130 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:291 GOQLFilter/whereexprrule.gpr.py:150 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:296 GOQLFilter/whereexprrule.gpr.py:170 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.py:301 GOQLFilter/whereexprrule.gpr.py:190 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:225 +msgid "_Find" +msgstr "" + +#: GOQLFilter/goql.py:227 +msgid "This updates the view with the current filter parameters." +msgstr "" + +#: GOQLFilter/goql.py:231 +msgid "Reset" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:237 +msgid "Define filter" +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:243 +msgid "Help" +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" diff --git a/GOQLFilter/whereexprrule.gpr.py b/GOQLFilter/whereexprrule.gpr.py index 543f7a6d5..0ddb2e2cb 100644 --- a/GOQLFilter/whereexprrule.gpr.py +++ b/GOQLFilter/whereexprrule.gpr.py @@ -32,7 +32,7 @@ "Matches people for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -52,7 +52,7 @@ "Matches families for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -72,7 +72,7 @@ "Matches events for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -92,7 +92,7 @@ "Matches places for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -112,7 +112,7 @@ "Matches repositories for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -132,7 +132,7 @@ "Matches sources for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -152,7 +152,7 @@ "Matches citations for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -172,7 +172,7 @@ "Matches media for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", @@ -192,7 +192,7 @@ "Matches notes for which the given gramps-object-query-language " "where-expression evaluates to true" ), - version="1.0.0", + version = '1.0.1', gramps_target_version="6.1", status=STABLE, fname="whereexprrule.py", From c57f3e0d8747e94cdf9bda63785866cd6cc63a62 Mon Sep 17 00:00:00 2001 From: GaryGriffin Date: Tue, 4 Aug 2026 08:56:45 -0700 Subject: [PATCH 090/156] Update translated strings for GOQLFilter --- po/addons.pot | 247 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-) diff --git a/po/addons.pot b/po/addons.pot index a0ea7b9cb..ccbd9228c 100644 --- a/po/addons.pot +++ b/po/addons.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -14913,6 +14913,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" From 9180aa8e4f256a00f64bed1a86f501fb45060e1d Mon Sep 17 00:00:00 2001 From: Avi Markovitz Date: Wed, 5 Aug 2026 20:02:03 +0200 Subject: [PATCH 091/156] Translated using Weblate (Hebrew) Currently translated at 99.1% (5473 of 5522 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ --- po/he.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/po/he.po b/po/he.po index fb8dc1dcb..5394d42b4 100644 --- a/po/he.po +++ b/po/he.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 07:42-0700\n" -"PO-Revision-Date: 2026-06-10 19:07+0000\n" +"PO-Revision-Date: 2026-07-03 16:19+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew \n" @@ -19,7 +19,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " "n % 10 == 0) ? 2 : 3));\n" -"X-Generator: Weblate 2026.6\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -18035,7 +18035,7 @@ msgstr "מי חוקר את מי?" #: NameSuite/.venv/lib/python3.12/site-packages/mypy/main.py:450 #, python-format msgid "%(prog)s: error: %(message)s\n" -msgstr "" +msgstr "%(prog)s: שגיאה: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 #, fuzzy From f7c2cba0444ce8182cd64ba4d83407c44691f53b Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Aug 2026 20:02:05 +0200 Subject: [PATCH 092/156] Update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/ --- po/ar.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/bg.po | 140 ++++++++++++++++++++++++++++++++++++++++++++- po/ca.po | 155 ++++++++++++++++++++++++++++++++++++++++++++++++-- po/cs.po | 143 +++++++++++++++++++++++++++++++++++++++++++++- po/cy.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/da.po | 157 +++++++++++++++++++++++++++++++++++++++++++++++++-- po/de.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/el.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/en_GB.po | 142 +++++++++++++++++++++++++++++++++++++++++++++- po/eo.po | 142 +++++++++++++++++++++++++++++++++++++++++++++- po/es.po | 157 +++++++++++++++++++++++++++++++++++++++++++++++++-- po/fi.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/fr.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/he.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/hr.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/hu.po | 144 ++++++++++++++++++++++++++++++++++++++++++++++- po/is.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/it.po | 152 ++++++++++++++++++++++++++++++++++++++++++++++++- po/ja.po | 146 ++++++++++++++++++++++++++++++++++++++++++++++- po/ka.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/ln.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/lt.po | 157 +++++++++++++++++++++++++++++++++++++++++++++++++-- po/lv.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/mn.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/nb.po | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-- po/ne.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/nl.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/nn.po | 140 ++++++++++++++++++++++++++++++++++++++++++++- po/oc.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/pl.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/pt_BR.po | 148 +++++++++++++++++++++++++++++++++++++++++++++++- po/pt_PT.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/ru.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/sk.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/sl.po | 139 ++++++++++++++++++++++++++++++++++++++++++++- po/sq.po | 139 ++++++++++++++++++++++++++++++++++++++++++++- po/sr.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/sv.po | 157 +++++++++++++++++++++++++++++++++++++++++++++++++-- po/tr.po | 148 +++++++++++++++++++++++++++++++++++++++++++++++- po/uk.po | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/vi.po | 140 ++++++++++++++++++++++++++++++++++++++++++++- po/zh_CN.po | 147 ++++++++++++++++++++++++++++++++++++++++++++++-- po/zh_HK.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- po/zh_TW.po | 138 ++++++++++++++++++++++++++++++++++++++++++++- 44 files changed, 6347 insertions(+), 142 deletions(-) diff --git a/po/ar.po b/po/ar.po index 7291ff910..455698fbf 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps-4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2014-06-29 15:50+0300\n" "Last-Translator: Munzir Taha (منذر طه) \n" "Language-Team: Arabic <>\n" @@ -1040,7 +1040,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15269,6 +15270,139 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +msgid "Done.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/bg.po b/po/bg.po index 3c5e429f5..c51bbc850 100644 --- a/po/bg.po +++ b/po/bg.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2026-03-14 05:09+0000\n" "Last-Translator: Iskren Petkov \n" "Language-Team: Bulgarian \n" "Language-Team: Catalan \n" "Language-Team: Czech \n" "Language-Team: Danish \n" "Language-Team: German \n" "Language-Team: Greek \n" "Language-Team: English (United Kingdom) \n" "Language-Team: Esperanto \n" "Language-Team: Spanish \n" "Language-Team: Finnish \n" "Language-Team: French \n" "Language-Team: Hebrew \n" "Language-Team: Croatian \n" "Language-Team: Hungarian \n" "Language-Team: Icelandic \n" "Language-Team: Italian \n" "Language-Team: Japanese \n" "Language-Team: Georgian \n" "Language-Team: Lithuanian \n" "Language-Team: Mongolian \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch \n" @@ -1046,7 +1046,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15383,6 +15384,141 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +msgid "Done.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +#, fuzzy +#| msgid "Theme preferences" +msgid "Model name:" +msgstr "Temainnstillinger" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/oc.po b/po/oc.po index b11636000..e9ddf2d64 100644 --- a/po/oc.po +++ b/po/oc.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -1029,7 +1029,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15256,6 +15257,139 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +msgid "Done.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/pl.po b/po/pl.po index 1fa3044ee..1eaae4293 100644 --- a/po/pl.po +++ b/po/pl.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2025-12-14 21:00+0000\n" "Last-Translator: WaldiS \n" "Language-Team: Polish \n" "Language-Team: Portuguese (Brazil) \n" "Language-Team: Portuguese (Portugal) \n" "Language-Team: Russian \n" "Language-Team: Slovak \n" "Language-Team: lugos slovenizacija \n" @@ -1072,7 +1072,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15406,6 +15407,140 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +#, fuzzy +msgid "Done.\n" +msgstr "brez" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/sq.po b/po/sq.po index 0a9c238ca..baf505a46 100644 --- a/po/sq.po +++ b/po/sq.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2008-11-13 21:00+0100\n" "Last-Translator: Vlora Jakupi \n" "Language-Team: \n" @@ -1082,7 +1082,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15372,6 +15373,140 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +#, fuzzy +msgid "Done.\n" +msgstr "Asgjë " + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/sr.po b/po/sr.po index a4186f409..75cf60282 100644 --- a/po/sr.po +++ b/po/sr.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2026-05-12 12:11+0000\n" "Last-Translator: Ранко Николић \n" "Language-Team: Serbian \n" "Language-Team: Swedish \n" "Language-Team: Turkish \n" "Language-Team: Ukrainian \n" "Language-Team: Vietnamese \n" "Language-Team: Chinese (Simplified Han script) \n" "Language-Team: Chinese (Hong Kong) <(nothing)>\n" @@ -1040,7 +1040,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15269,6 +15270,139 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +msgid "Done.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" diff --git a/po/zh_TW.po b/po/zh_TW.po index 24a111b8c..69ee34a51 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 07:42-0700\n" +"POT-Creation-Date: 2026-07-03 09:18-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (traditional) \n" @@ -1040,7 +1040,8 @@ msgstr "" msgid "Type a message..." msgstr "" -#: ChatWithTree/ChatWithTree.py:153 +#: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 +#: GrampsAssistant/grampsassistant.py:920 msgid "Send" msgstr "" @@ -15269,6 +15270,139 @@ msgid "" "in user home directory ''%s''?" msgstr "" +#: GrampsAssistant/grampsassistant.gpr.py:26 +#: GrampsAssistant/grampsassistant.py:1417 +msgid "Gramps Assistant" +msgstr "" + +#: GrampsAssistant/grampsassistant.gpr.py:27 +msgid "AI assistant for querying your Gramps family tree" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:58 +msgid "" +"You are a helpful genealogy assistant with access to the user's Gramps " +"database. Answer questions about people, families, events, and " +"relationships. When you need information from the database, call the " +"provided tools — never write code, simulate results, or make up data. If no " +"tool exists for the requested information, say so plainly." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:209 +msgid "Gramps Assistant settings" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:213 +msgid "Clear conversation and context" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:388 +#: GrampsAssistant/grampsassistant.py:749 +msgid "Gramps Assistant:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:390 +msgid "" +"Ask me anything about the Gramps program or your specific Gramps family " +"tree. Use the ⚙ button to configure the AI.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:720 +msgid "" +"\n" +"No model configured. Please click the Settings button to choose a backend " +"and model before chatting.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:755 +msgid "Thinking..." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:824 +#, python-brace-format +msgid "" +"API key error: the environment variable {var} is not set or is invalid. Set " +"it before launching Gramps:\n" +" export {var}=your-key-here" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:830 +msgid "" +"API key error: this provider requires an API key. Open Settings and enter " +"the environment variable name for your API key (e.g. OPENAI_API_KEY)." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:973 +msgid "Done.\n" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1203 +msgid "System Prompt:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1217 +msgid "Simplify tools (recommended for smaller/local models)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1221 +msgid "" +"When enabled, only the tools relevant to your question are sent to the " +"model. This improves performance with smaller local models." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1230 +msgid "Use Local Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1249 +msgid "" +"URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1257 +msgid "model name (leave blank for LM Studio / llama.cpp)" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1260 +msgid "" +"Model to request from the local server. Required for Ollama (e.g. llama3.1). " +"Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1267 +#: GrampsAssistant/grampsassistant.py:1311 +msgid "Model:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1273 +msgid "Use Foundational Model" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1315 +msgid "e.g. OPENAI_API_KEY" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1317 +msgid "Name of the environment variable holding your API key." +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1319 +msgid "API key env var:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1322 +msgid "Backend:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1334 +msgid "Base URL:" +msgstr "" + +#: GrampsAssistant/grampsassistant.py:1338 +msgid "Model name:" +msgstr "" + #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" msgstr "" From 47e601260082b1b23fc35f682ced16a5c4a953dd Mon Sep 17 00:00:00 2001 From: Avi Markovitz Date: Wed, 5 Aug 2026 20:02:06 +0200 Subject: [PATCH 093/156] Translated using Weblate (Hebrew) Currently translated at 99.9% (5547 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 99.4% (5520 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 99.1% (5502 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 99.0% (5495 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 98.8% (5485 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 98.8% (5484 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 98.7% (5479 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 98.6% (5474 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ --- po/he.po | 247 ++++++++++++++++++++++--------------------------------- 1 file changed, 98 insertions(+), 149 deletions(-) diff --git a/po/he.po b/po/he.po index 51dd1ac4b..5465b0b7e 100644 --- a/po/he.po +++ b/po/he.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-07-03 16:19+0000\n" +"PO-Revision-Date: 2026-07-29 16:56+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew \n" @@ -19,7 +19,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " "n % 10 == 0) ? 2 : 3));\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -7712,7 +7712,7 @@ msgstr "6. יוחסה לראש המשפחה" #: Form/form_ca.xml.h:473 Form/form_ca.xml.h:684 msgid "7. Single, married, widowed, divorced or legally separated" -msgstr "7. רווק, נשוי, אלמן, התגרש או חוקית פרוד" +msgstr "7. רווק, נשוי, אלמן, התגרש או פרוד על פי חוק" #: Form/form_ca.xml.h:474 Form/form_ca.xml.h:685 Form/form_ca.xml.h:773 msgid "BirthMonth" @@ -8462,7 +8462,7 @@ msgstr "5. יוחסה לראש המשפחה" #: Form/form_ca.xml.h:772 msgid "6. Single, married, widowed, divorced or legally separated" -msgstr "6. רווק, נשוי, אלמן, התגרש או חוקית פרוד" +msgstr "6. רווק, נשוי, אלמן, התגרש או פרוד על פי חוק" #: Form/form_ca.xml.h:774 msgid "7. Month of birth" @@ -8554,7 +8554,7 @@ msgstr "10. מין" #: Form/form_ca.xml.h:825 msgid "11. Single, married, widowed, divorced or legally separated" -msgstr "11. רווק, נשוי, אלמן, התגרש או חוקית פרוד" +msgstr "11. רווק, נשוי, אלמן, התגרש או פרוד על פי חוק" #: Form/form_ca.xml.h:827 msgid "12. Age at last birthday" @@ -15603,14 +15603,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "גרסת גרמפס" +msgstr "סייען גרמפס" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "סייען בינה מלאכותית לשאילתות אילן־יוחסין גרמפס" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15620,29 +15618,32 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"פעל כסייען חקר־יוחסין יעיל עם גישה למסד הנתוניגרמפס של המשתמש. ענה על שאלות " +"לגבי אנשים, משפחות, אירועים וקשרי־קרבה. אם אתה זקוק למידע ממסד הנתונים, פנה " +"לכלים המסופקים – לעולם אל תכתוב קוד, אל תמציא דמיוניות תוצאות ואל תמציא " +"נתונים שלא קיימים במסד הנתונים. אם לא קיים כלי עבור המידע המבוקש, אמור זאת " +"במפורש." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "הגדרות סגנון נוספות:" +msgstr "הגדרות סייען גרמפס" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "ניקוי שיחה ותוכן" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "גרסת גרמפס" +msgstr "סייען גרמפס:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"ניתן לשאול אותי כל דבר בנוגע לתוכנית גרמפס או על אילן־היחוסין מסויים. יש " +"להשתמש בלחצן ⚙ כדי להגדיר את הבינה המלאכותית.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15650,10 +15651,12 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"לא תוצר מודל. נא להקיש על לחצן הגדרות לבחירת גב־המערכת ומודל לפני שיחוח.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "חשיבה..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15662,95 +15665,94 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"שגיאת מפתח API: משתנה הסביבה {var} לא הוגדר או לא תקין. נא להגדירו לפני " +"שיגור גרמפס:\n" +" export {var}=הזנת-המפתח-כאן" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"שגיאת מפתח API: ספק שרות זה דורש מפתח API. נא לפתוח את תפריט ההגדרות ולהזין " +"את שם משתנה הסביבה למפתח ה API (לדוגמה, OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "בוצע!\n" +msgstr "בוצע.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "הנחיית מערכת:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "פישוט כלים (מומלץ לדגמים קטנים/מקומיים)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"כאשר מאופשר, רק הכלים הרלוונטיים לשאלה שנשאלה נשלחים למודל. זה משפר ביצועים " +"במודלים מקומיים קטנים." #: GrampsAssistant/grampsassistant.py:1230 -#, fuzzy -#| msgid "Mistral Model" msgid "Use Local Model" -msgstr "דגם Mistral" +msgstr "שימוש בדגם מקומי" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"כתובת URL של שרת מקומי תואם OpenAI. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "שם מודל (להשאיר ריק עבור LM Studio / llama.cpp)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"מודל לבקשה מהשרת המקומי. נדרש עבור Ollama (לדוגמה llama3.1). להשאיר ריק עבור " +"LM Studio או llama.cpp, שמשתמשים בכל מודל שנטען." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "חדיש" +msgstr "דגם:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "תאריך יסוד:" +msgstr "שימוש בדגם יסוד:" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "לדוגמה OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "שם משתנה הסביבה שמחזיק במפתח ה–API." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "משתנה' סביבת מפתח API:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "גב־מערכת:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "כתובת URL של אתר מרשתת" +msgstr "כתובת URL בסיסית:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "זוגאי שם:" +msgstr "שם דגם:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -18190,29 +18192,25 @@ msgid "%(prog)s: error: %(message)s\n" msgstr "%(prog)s: שגיאה: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Given and Patronymic Names" -msgstr "שמות פטרוניים:" +msgstr "ביקורת שמות פרטיים ושמות פטרונימיים" #: NameSuite/name_processor.gpr.py:9 msgid "" "Tools to rename given name, audit and infer patronymic (East Slavic) names." -msgstr "" +msgstr "כלים לשינוי שם פרטי, ביקורת והסקת שמות פטרונימיים (שמות מזרח סלאביים)." #: NameSuite/name_processor.gpr.py:25 NameSuite/name_processor.gpr.py:37 -#, fuzzy -#| msgid "Patronymic names:" msgid "Patronymic Suggestion" -msgstr "שמות פטרוניים:" +msgstr "הצעת שם פטרוני" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." -msgstr "" +msgstr "הצעת שמות פטרונימיים (מזרח סלאביים) בזמן אמת תוך כדי ניווט." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" -msgstr "" +msgstr "להשתמש" #: NameSuite/name_processor/views/gramplet.py:32 #, python-brace-format @@ -18221,222 +18219,173 @@ msgid "" "Suggested: {0}\n" "Based on father: {1}" msgstr "" +"זוהה שם פטרונימי חסר.\n" +"הוצע: {0}\n" +"מבוסס על אב: {1}" #: NameSuite/name_processor/views/gramplet.py:34 msgid "Navigate to an individual to check patronymic status." -msgstr "" +msgstr "ניוט לאדם כדי לבדוק מצב שם פטרונימי." #: NameSuite/name_processor/views/gramplet.py:35 -#, fuzzy -#| msgid "No Active Person set." msgid "No active person selected." -msgstr "לא הוגדר 'אדם פעיל'." +msgstr "לא נבחר אדם פעיל." #: NameSuite/name_processor/views/gramplet.py:37 msgid "" "Patronymic inference can't be inferred for non-binary or unknown genders." -msgstr "" +msgstr "לא ניתן להסיק שם פטרונימי למגדרים לא בינאריים או לא ידועים." #: NameSuite/name_processor/views/gramplet.py:40 msgid "Individual already has a recorded patronymic." -msgstr "" +msgstr "לאדם כבר יש שם פטרונימי רשום." #: NameSuite/name_processor/views/gramplet.py:43 msgid "No attached father found in database family records." -msgstr "" +msgstr "לא נמצא אב משוייך במסד נתוני רשומות משפחה." #: NameSuite/name_processor/views/gramplet.py:46 msgid "Father lacks a recorded first name." -msgstr "" +msgstr "לאב חסרה רשומת שם פרטי." #: NameSuite/name_processor/views/gramplet.py:49 msgid "Could not generate valid morphology patterns." -msgstr "" +msgstr "לא ניתן ליצור דפוסי מורפולוגיה תקינות." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "" +msgstr "פטרונימי הוחל בהצלחה!" #: NameSuite/name_processor/views/gramplet.py:77 -#, fuzzy -#| msgid "🔍 AI Suggestions" msgid "Apply Suggestion" -msgstr "🔍 הצעות בינה מלאכותית" +msgstr "החלת הצעה" #: NameSuite/name_processor/views/tool.py:92 msgid "Infer East Slavic Patronymics" -msgstr "" +msgstr "להסיק פטרוניים מזרח סלביים" #: NameSuite/name_processor/views/tool.py:112 -#, fuzzy -#| msgid "Checking Given Names" msgid "Rename Given Names" -msgstr "בדיקת שמות פרטיים" +msgstr "שינוי שמות פרטיים" #: NameSuite/name_processor/views/tool.py:115 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Patronymics" -msgstr "שמות פטרוניים:" +msgstr "ביקורת שמות פטרוניים" #: NameSuite/name_processor/views/tool_audit_tab.py:68 msgid "Auditing Settings" -msgstr "" +msgstr "הגררות ביקורת" #: NameSuite/name_processor/views/tool_audit_tab.py:76 -#, fuzzy -#| msgid "Place of Record" msgid "All Records" -msgstr "מקום רישום" +msgstr "כל הרשומות" #: NameSuite/name_processor/views/tool_audit_tab.py:77 -#, fuzzy -#| msgid "Male line" msgid "Males Only" -msgstr "קו זכרי" +msgstr "זכרים בלבד" #: NameSuite/name_processor/views/tool_audit_tab.py:78 -#, fuzzy -#| msgid "Female line" msgid "Females Only" -msgstr "קו נקבי" +msgstr "נקבות בלבד" #: NameSuite/name_processor/views/tool_audit_tab.py:82 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules..." -msgstr "הגדרה" +msgstr "תצור כללים..." #: NameSuite/name_processor/views/tool_audit_tab.py:87 msgid "Match Pre-Revolutionary Orthography" -msgstr "" +msgstr "התאמת אורתוגרפיה טרום מהפכה" #: NameSuite/name_processor/views/tool_audit_tab.py:95 -#, fuzzy -#| msgid "Edit tags" msgid "Audit Database" -msgstr "עריכה תגים" +msgstr "בירורת מסד־נתונים" #: NameSuite/name_processor/views/tool_audit_tab.py:117 -#, fuzzy -#| msgid "Select the graph direction." msgid "Select All Safe Corrections" -msgstr "בחירת גרף כיוון." +msgstr "בחירת כל התיקונים הבטוחים" #: NameSuite/name_processor/views/tool_audit_tab.py:122 #: NameSuite/name_processor/views/tool_rename_tab.py:116 -#, fuzzy -#| msgid "Apply to selected places" msgid "Apply Selected Corrections" -msgstr "החלה על המקום שנבחר" +msgstr "החלת תיקונים שנבחרו" #: NameSuite/name_processor/views/tool_audit_tab.py:162 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules" -msgstr "הגדרה" +msgstr "תצור כללים" #: NameSuite/name_processor/views/tool_audit_tab.py:186 #: NameSuite/name_processor/views/tool_rename_tab.py:156 -#, fuzzy -#| msgid "India" msgid "Individual" -msgstr "הודו" +msgstr "פרט" #: NameSuite/name_processor/views/tool_audit_tab.py:190 #: NameSuite/name_processor/views/tool_rename_tab.py:158 -#, fuzzy -#| msgid "Current sort" msgid "Current" -msgstr "מיון נוכחי" +msgstr "נוכחי" #: NameSuite/name_processor/views/tool_audit_tab.py:193 -#, fuzzy -#| msgid "Section" msgid "Correction" -msgstr "מקטע" +msgstr "תיקון" #: NameSuite/name_processor/views/tool_audit_tab.py:200 -#, fuzzy -#| msgid "Configure" msgid "Conf" -msgstr "הגדרה" +msgstr "תצור" #: NameSuite/name_processor/views/tool_audit_tab.py:201 -#, fuzzy -#| msgid "Event Year" msgid "Ref Year" -msgstr "שנת האירוע" +msgstr "שנת אזכור" #: NameSuite/name_processor/views/tool_audit_tab.py:204 -#, fuzzy -#| msgid "Relation" msgid "Explanation" -msgstr "יוחסה" +msgstr "הסבר" #: NameSuite/name_processor/views/tool_audit_tab.py:243 -#, fuzzy -#| msgid "Completed?" msgid "Audit Complete!" -msgstr "הושלם?" +msgstr "ביקורת הושלמה!" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "Result" msgid "No Results" -msgstr "תוצאה" +msgstr "אין תוצאות" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "No persons found..." msgid "No issues found." -msgstr "אין אנשים נמצא..." +msgstr "לא נמצאו סוגיות." #: NameSuite/name_processor/views/tool_rename_tab.py:63 -#, fuzzy -#| msgid "Search result places" msgid "Search and Replace Options" -msgstr "תוצאות חיפוש מקומות" +msgstr "אפשרויות חיפוש והחלפה" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Spouse name:" msgid "Source Name:" -msgstr "זוגאי שם:" +msgstr "שם מקור:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" -msgstr "" +msgstr "לדוגמה Иоанн" #: NameSuite/name_processor/views/tool_rename_tab.py:75 -#, fuzzy -#| msgid "Street Name" msgid "Target Name:" -msgstr "רחוב שם" +msgstr "שם יעד:" #: NameSuite/name_processor/views/tool_rename_tab.py:77 msgid "e.g. Иван" -msgstr "" +msgstr "לדוגמה Иван" #: NameSuite/name_processor/views/tool_rename_tab.py:80 msgid "Match Mode:" -msgstr "" +msgstr "אופן התאמה:" #: NameSuite/name_processor/views/tool_rename_tab.py:82 msgid "Exact Match" -msgstr "" +msgstr "התאמה מדוייקת" #: NameSuite/name_processor/views/tool_rename_tab.py:83 -#, fuzzy -#| msgid "SubDistrict" msgid "Substring" -msgstr "תת מחוז" +msgstr "מחרוזת משנה" #: NameSuite/name_processor/views/tool_rename_tab.py:84 -#, fuzzy -#| msgid "Allow regular expressions." msgid "Regular Expression" -msgstr "לאפשר ביטויים סדירים." +msgstr "ביטוי רגולרי" #: NameSuite/name_processor/views/tool_rename_tab.py:88 #, fuzzy @@ -18446,7 +18395,7 @@ msgstr "מפתח שמות" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" -msgstr "" +msgstr "שימור שם מקורי כחלופה" #: NameSuite/name_processor/views/tool_rename_tab.py:160 #, fuzzy @@ -21060,11 +21009,11 @@ msgid "" "Assistant in order to export all data.\n" "\n" msgstr "" -"גיבוי ל־Gramps XML בגרסאות גרמפס האחרונות, " -"'יצירת גיבוי...' נמצא בתפריט אילן־יוחסין. אחרת יש להשתמש ב־'ייצוא...' באותו " -"תפריט, אך להסיר את סימון אפשרויות הפרטיות במסייע הייצוא כדי לייצא את כל " -"הנתונים.\n" +"גיבוי ל־Gramps XML בגרסאות גרמפס " +"האחרונות, 'יצירת גיבוי...' נמצא בתפריט אילן־יוחסין. אחרת יש להשתמש ב" +"־'ייצוא...' באותו תפריט, אך להסיר את סימון אפשרויות הפרטיות בסייען הייצוא " +"כדי לייצא את כל הנתונים.\n" "\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:336 From 6778b46d26eda921024d134767d074ae1f1fd950 Mon Sep 17 00:00:00 2001 From: Stephan Paternotte Date: Wed, 5 Aug 2026 20:02:06 +0200 Subject: [PATCH 094/156] Translated using Weblate (Dutch) Currently translated at 100.0% (5549 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/nl/ --- po/nl.po | 246 ++++++++++++++++++++++--------------------------------- 1 file changed, 100 insertions(+), 146 deletions(-) diff --git a/po/nl.po b/po/nl.po index 7e383a0b6..1b79d6ddb 100644 --- a/po/nl.po +++ b/po/nl.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-04 05:01+0000\n" +"PO-Revision-Date: 2026-07-04 17:49+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch \n" @@ -22,7 +22,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.6\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -15934,14 +15934,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Gramps-versie" +msgstr "Gramps Assistent" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "AI-assistent voor het opvragen van uw Gramps-stamboom" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15951,29 +15949,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"U bent een behulpzame genealogische assistent met toegang tot de Gramps-" +"database van de gebruiker. Beantwoord vragen over mensen, gezinnen, " +"gebeurtenissen en relaties. Wanneer u informatie uit de database nodig " +"heeft, roept u de meegeleverde tools aan — schrijf nooit code, simuleer geen " +"resultaten of verzin gegevens. Als er geen hulpmiddel bestaat voor de " +"gevraagde informatie, zeg dat dan duidelijk." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Extra stijlinstellingen:" +msgstr "Gramps Assistent instellingen" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Gesprek en context wissen" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Gramps-versie" +msgstr "Gramps Assistent:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Vraag me iets over het Gramps programma of uw specifieke Gramps stamboom. " +"Gebruik de ⚙-knop om de AI te configureren\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15981,10 +15983,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Geen model geconfigureerd. Klik op de knop Instellingen om een backend en " +"model te kiezen voor het chatten.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Nadenken…" #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15993,95 +15998,97 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"API-sleutelfout: de omgevingsvariabele {var} is niet ingesteld of ongeldig. " +"Stel deze in voordat u Gramps start:\n" +" export {var}=your-key-here" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"API-sleutelfout: deze provider vereist een API-sleutel. Open Instellingen en " +"voer de naam van de omgevingsvariabele in voor uw API-sleutel (bijv. " +"OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Uitgevoerd!\n" +msgstr "Klaar!\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Systeemprompt:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Vereenvoudigingstools (aanbevolen voor kleinere/lokale modellen)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Indien ingeschakeld, worden alleen de tools die relevant zijn voor uw vraag " +"naar het model verzonden. Dit verbetert de prestaties bij kleinere lokale " +"modellen." #: GrampsAssistant/grampsassistant.py:1230 -#, fuzzy -#| msgid "Mistral Model" msgid "Use Local Model" -msgstr "Mistral-model" +msgstr "Gebruik Lokaal Model" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"URL van een lokale OpenAI-compatibele server. Ollama: http://localhost:11434" +" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "modelname (leeg laten voor LM Studio / llama.cpp)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Model op te vragen bij de lokale server. Vereist voor Ollama (bijv. llama3.1)" +". Laat leeg voor LM Studio of llama.cpp, die welk model dan ook wordt " +"geladen gebruiken." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Modern" +msgstr "Model:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Oprichtingsdatum:" +msgstr "Gebruik Foundational Model" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "bijv. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "Naam van de omgevingsvariabele die uw API-sleutel bevat." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "API-sleutel omg.var:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Back-end:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "Website-URL" +msgstr "Basis-URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Naam echtgenoot:" +msgstr "Modelnaam:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -18611,32 +18618,30 @@ msgstr "Wie (onder)zoekt wie?" #: NameSuite/.venv/lib/python3.12/site-packages/mypy/main.py:450 #, python-format msgid "%(prog)s: error: %(message)s\n" -msgstr "" +msgstr "%(prog)s: fout: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Given and Patronymic Names" -msgstr "Patroniemen:" +msgstr "Voornamen en Patroniemen controleren" #: NameSuite/name_processor.gpr.py:9 msgid "" "Tools to rename given name, audit and infer patronymic (East Slavic) names." msgstr "" +"Hulpmiddelen om de voornaam te hernoemen, te controleren en patroniemnamen " +"(Oost-Slavisch) af te leiden." #: NameSuite/name_processor.gpr.py:25 NameSuite/name_processor.gpr.py:37 -#, fuzzy -#| msgid "Patronymic names:" msgid "Patronymic Suggestion" -msgstr "Patroniemen:" +msgstr "Patroniemsuggestie" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." -msgstr "" +msgstr "Stelt realtime (Oost-Slavisch) patroniemnamen voor terwijl u navigeert." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" -msgstr "" +msgstr "Gebruiken" #: NameSuite/name_processor/views/gramplet.py:32 #, python-brace-format @@ -18645,238 +18650,187 @@ msgid "" "Suggested: {0}\n" "Based on father: {1}" msgstr "" +"Ontbrekend patroniem gedetecteerd.\n" +"Voorgesteld: {0}\n" +"Gebaseerd op vader: {1}" #: NameSuite/name_processor/views/gramplet.py:34 msgid "Navigate to an individual to check patronymic status." -msgstr "" +msgstr "Navigeer naar een individu om de patroniemstatus te controleren." #: NameSuite/name_processor/views/gramplet.py:35 -#, fuzzy -#| msgid "No Active Person set." msgid "No active person selected." -msgstr "Geen actieve persoon ingesteld." +msgstr "Geen actieve persoon geselecteerd.." #: NameSuite/name_processor/views/gramplet.py:37 msgid "" "Patronymic inference can't be inferred for non-binary or unknown genders." msgstr "" +"Patronieminferentie kan niet worden afgeleid voor non-binaire of onbekende " +"geslachten." #: NameSuite/name_processor/views/gramplet.py:40 msgid "Individual already has a recorded patronymic." -msgstr "" +msgstr "Individu heeft al een opgenomen patroniem." #: NameSuite/name_processor/views/gramplet.py:43 msgid "No attached father found in database family records." -msgstr "" +msgstr "Geen bijgevoegde vader gevonden in databaseregistraties." #: NameSuite/name_processor/views/gramplet.py:46 msgid "Father lacks a recorded first name." -msgstr "" +msgstr "Vader mist een geregistreerde voornaam." #: NameSuite/name_processor/views/gramplet.py:49 msgid "Could not generate valid morphology patterns." -msgstr "" +msgstr "Kon geen geldige morfologiepatronen genereren." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "" +msgstr "Patroniem succesvol toegepast!" #: NameSuite/name_processor/views/gramplet.py:77 -#, fuzzy -#| msgid "🔍 AI Suggestions" msgid "Apply Suggestion" -msgstr "🔍 AI-suggesties" +msgstr "Suggestie toepassen" #: NameSuite/name_processor/views/tool.py:92 msgid "Infer East Slavic Patronymics" -msgstr "" +msgstr "Oost-Slavische patroniemen herleiden" #: NameSuite/name_processor/views/tool.py:112 -#, fuzzy -#| msgid "Checking Given Names" msgid "Rename Given Names" -msgstr "Voornamen controleren" +msgstr "Voornamen vervangen" #: NameSuite/name_processor/views/tool.py:115 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Patronymics" -msgstr "Patroniemen:" +msgstr "Patroniemen controleren" #: NameSuite/name_processor/views/tool_audit_tab.py:68 msgid "Auditing Settings" -msgstr "" +msgstr "Controle-instellingen" #: NameSuite/name_processor/views/tool_audit_tab.py:76 -#, fuzzy -#| msgid "Place of Record" msgid "All Records" -msgstr "Plaats van registratie" +msgstr "Alle registraties" #: NameSuite/name_processor/views/tool_audit_tab.py:77 -#, fuzzy -#| msgid "Male line" msgid "Males Only" -msgstr "Mannelijke lijn" +msgstr "Alleen mannen" #: NameSuite/name_processor/views/tool_audit_tab.py:78 -#, fuzzy -#| msgid "Female line" msgid "Females Only" -msgstr "Vrouwelijke lijn" +msgstr "Alleen vrouwen" #: NameSuite/name_processor/views/tool_audit_tab.py:82 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules..." -msgstr "Configureer" +msgstr "Regels configureren…" #: NameSuite/name_processor/views/tool_audit_tab.py:87 msgid "Match Pre-Revolutionary Orthography" -msgstr "" +msgstr "Overeenkomst met Pre-Revolutionaire Orthografie" #: NameSuite/name_processor/views/tool_audit_tab.py:95 -#, fuzzy -#| msgid "Edit tags" msgid "Audit Database" -msgstr "Labels bewerken" +msgstr "Database controleren" #: NameSuite/name_processor/views/tool_audit_tab.py:117 -#, fuzzy -#| msgid "Select the graph direction." msgid "Select All Safe Corrections" -msgstr "Selecteer de grafiekrichting." +msgstr "Alle veilige correcties selecteren" #: NameSuite/name_processor/views/tool_audit_tab.py:122 #: NameSuite/name_processor/views/tool_rename_tab.py:116 -#, fuzzy -#| msgid "Apply to selected places" msgid "Apply Selected Corrections" -msgstr "Toepassen op geselecteerde plaatsen" +msgstr "Alle geselecteerde correcties toepassen" #: NameSuite/name_processor/views/tool_audit_tab.py:162 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules" -msgstr "Configureer" +msgstr "Regels configureren" #: NameSuite/name_processor/views/tool_audit_tab.py:186 #: NameSuite/name_processor/views/tool_rename_tab.py:156 -#, fuzzy -#| msgid "India" msgid "Individual" -msgstr "India" +msgstr "Individu" #: NameSuite/name_processor/views/tool_audit_tab.py:190 #: NameSuite/name_processor/views/tool_rename_tab.py:158 -#, fuzzy -#| msgid "Current sort" msgid "Current" -msgstr "Huidige sortering" +msgstr "Huidige" #: NameSuite/name_processor/views/tool_audit_tab.py:193 -#, fuzzy -#| msgid "Section" msgid "Correction" -msgstr "Sectie" +msgstr "Correctie" #: NameSuite/name_processor/views/tool_audit_tab.py:200 -#, fuzzy -#| msgid "Configure" msgid "Conf" -msgstr "Configureer" +msgstr "Conf" #: NameSuite/name_processor/views/tool_audit_tab.py:201 -#, fuzzy -#| msgid "Event Year" msgid "Ref Year" -msgstr "Jaar gebeurtenis" +msgstr "RefJaar" #: NameSuite/name_processor/views/tool_audit_tab.py:204 -#, fuzzy -#| msgid "Relation" msgid "Explanation" -msgstr "Relatie" +msgstr "Verklaring" #: NameSuite/name_processor/views/tool_audit_tab.py:243 -#, fuzzy -#| msgid "Completed?" msgid "Audit Complete!" -msgstr "Afgerond?" +msgstr "Controle voltooid!" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "Result" msgid "No Results" -msgstr "Uitkomst" +msgstr "Geen resultaten" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "No persons found..." msgid "No issues found." -msgstr "Geen personen gevonden..." +msgstr "Geen problemen gevonden." #: NameSuite/name_processor/views/tool_rename_tab.py:63 -#, fuzzy -#| msgid "Search result places" msgid "Search and Replace Options" -msgstr "Zoekresultaat plaatsen" +msgstr "Zoek en vervang opties" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Spouse name:" msgid "Source Name:" -msgstr "Naam echtgenoot:" +msgstr "Bronnaam:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" -msgstr "" +msgstr "bijv. Иоанн" #: NameSuite/name_processor/views/tool_rename_tab.py:75 -#, fuzzy -#| msgid "Street Name" msgid "Target Name:" -msgstr "Straatnaam" +msgstr "Doelnaam:" #: NameSuite/name_processor/views/tool_rename_tab.py:77 msgid "e.g. Иван" -msgstr "" +msgstr "bijv. Иван" #: NameSuite/name_processor/views/tool_rename_tab.py:80 msgid "Match Mode:" -msgstr "" +msgstr "Overeenkomstmodus:" #: NameSuite/name_processor/views/tool_rename_tab.py:82 msgid "Exact Match" -msgstr "" +msgstr "Exacte overeenkomst" #: NameSuite/name_processor/views/tool_rename_tab.py:83 -#, fuzzy -#| msgid "SubDistrict" msgid "Substring" -msgstr "SubDistrict" +msgstr "Deelreeks" #: NameSuite/name_processor/views/tool_rename_tab.py:84 -#, fuzzy -#| msgid "Allow regular expressions." msgid "Regular Expression" -msgstr "Sta reguliere expressies toe." +msgstr "Reguliere expressies" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Index of Names" msgid "Scan for Names" -msgstr "Index van namen" +msgstr "Zoeken naar namen" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" -msgstr "" +msgstr "Oorspronkelijke naam als alternatief behouden" #: NameSuite/name_processor/views/tool_rename_tab.py:160 -#, fuzzy -#| msgid "Proposed sort" msgid "Proposed" -msgstr "Voorgestelde sortering" +msgstr "Voorgesteld" #: NetworkChart/NetworkChart.gpr.py:24 msgid "Network Chart" From 9e753ffed415ac584a836156709dc0370e264bbb Mon Sep 17 00:00:00 2001 From: Pedro Albuquerque Date: Wed, 5 Aug 2026 20:02:07 +0200 Subject: [PATCH 095/156] Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (5549 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/pt_PT/ --- po/pt_PT.po | 83 +++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/po/pt_PT.po b/po/pt_PT.po index 16ca5ccc3..d29d2869d 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-07-03 15:58+0000\n" +"PO-Revision-Date: 2026-07-04 17:49+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) \n" @@ -15795,14 +15795,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Versão Gramps" +msgstr "Assistente Gramps" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "Assistente de IA para consultar a sua árvore genealógica no Gramps" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15812,29 +15810,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"É um assistente de genealogia prestável, com acesso à base de dados do " +"Gramps. Responde a perguntas sobre pessoas, famílias, eventos e relações. " +"Quando precisar de informações da base de dados, recorra às ferramentas " +"disponibilizadas — nunca escreva código, simule resultados ou invente dados. " +"Se não existir nenhuma ferramenta para a informação solicitada, di-lo " +"claramente." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Definições de estilo extra:" +msgstr "Definições do assistente Gramps" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Conversa e contexto claros" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Versão Gramps" +msgstr "Assistente Gramps:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Pergunte-me o que quiser sobre o programa Gramps ou sobre a sua árvore " +"genealógica específica no Gramps. Utilize o botão ⚙ para configurar a IA.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15842,10 +15844,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Não há nenhum modelo configurado. Clique no botão «Definições» para escolher " +"um motor e um modelo antes de iniciar a conversa.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "A pensar..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15854,95 +15859,97 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"Erro na chave API: a variável de ambiente {var} não está definida ou é " +"inválida. Defina-a antes de iniciar o Gramps:\n" +" export {var}=a-sua-chave-aqui" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"Erro na chave API: este fornecedor requer uma chave API. Abra as definições " +"e introduza o nome da variável de ambiente correspondente à sua chave API " +"(e.g. OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Feito!\n" +msgstr "Feito.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Mensagem do sistema:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Simplificar ferramentas (recomendado para modelos menores/locais)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Quando activa, só as ferramentas relevantes para a sua pergunta são enviadas " +"para o modelo. Melhora o desempenho com modelos locais menores." #: GrampsAssistant/grampsassistant.py:1230 -#, fuzzy -#| msgid "Mistral Model" msgid "Use Local Model" -msgstr "Modelo Mistral" +msgstr "Usar modelo local" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"URL de um servidor local compatível com a OpenAI. Ollama: http://" +"localhost:11434 LM Studio: http://localhost:1234 llama.cpp: http://" +"localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "nome do modelo (deixe em branco para LM Studio / llama.cpp)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Modelo a solicitar ao servidor local. Obrigatório para Ollama (e.g., " +"llama3.1). Deixe em branco para LM Studio ou llama.cpp, que utilizam " +"qualquer modelo que esteja carregado." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Moderno" +msgstr "Modelo:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Data de fundação:" +msgstr "Utilizar o modelo fundamental" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "e.g. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "Nome da variável de ambiente que contém a sua chave API." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "Variável de ambiente da chave API:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Motor:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "URL do sítio web" +msgstr "URL base:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Nome do cônjuge:" +msgstr "Nome do modelo:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" From 8ea1c3293336a7f6e96702373a6c8103ab4ce450 Mon Sep 17 00:00:00 2001 From: Milan Date: Wed, 5 Aug 2026 20:02:07 +0200 Subject: [PATCH 096/156] Translated using Weblate (Slovak) Currently translated at 98.7% (5477 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sk/ Translated using Weblate (Slovak) Currently translated at 98.7% (5477 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sk/ Translated using Weblate (Slovak) Currently translated at 98.6% (5475 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sk/ --- po/sk.po | 257 +++++++++++++++++++++++-------------------------------- 1 file changed, 107 insertions(+), 150 deletions(-) diff --git a/po/sk.po b/po/sk.po index 0af54541f..5c6d3c34b 100644 --- a/po/sk.po +++ b/po/sk.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-29 14:56+0000\n" +"PO-Revision-Date: 2026-07-09 15:32+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak \n" @@ -21,7 +21,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -3832,7 +3832,7 @@ msgstr "MIN_C_WIDTH" #: DescendantsLines/DescendantsLines.py:1774 msgid "The number of ??? min width" -msgstr "Minimálna šírka ???" +msgstr "Veľkosť ??? min. šírky" #: DescendantsLines/DescendantsLines.py:1779 msgid "Space around text" @@ -9160,7 +9160,7 @@ msgstr "Číslo osvedčenia" #: Form/form_us.xml.h:2344 Form/form_us.xml.h:2402 Form/form_us.xml.h:2428 #: Form/form_us.xml.h:2460 msgid "Entry Number" -msgstr "Číslo záznamu" +msgstr "Číslo zápisu" #: Form/form_gb.xml.h:3 Form/form_gb.xml.h:40 Form/form_gb.xml.h:196 #: Form/form_gb.xml.h:230 Form/form_gb.xml.h:329 Form/form_gb.xml.h:365 @@ -10242,7 +10242,7 @@ msgstr "Meno matky manžela/manželky" #: Form/form_pl.xml.h:18 msgid "Record Number" -msgstr "" +msgstr "Číslo záznamu" #: Form/form_pl.xml.h:19 msgid "Volume" @@ -10254,11 +10254,11 @@ msgstr "Čislo domu" #: Form/form_pl.xml.h:28 msgid "Volume Beginning Year" -msgstr "" +msgstr "Zväzok začínajúci rokom" #: Form/form_pl.xml.h:29 msgid "Volume Ending Year" -msgstr "" +msgstr "Zväzok končiaci rokom" #: Form/form_us.xml.h:1 Form/form_us.xml.h:14 Form/form_us.xml.h:35 #: Form/form_us.xml.h:56 Form/form_us.xml.h:96 Form/form_us.xml.h:165 @@ -15805,18 +15805,18 @@ msgid "" "in user home directory ''%s''?" msgstr "" "Prepísať kmz/kml súbor ''%s''\n" -"v domovskom adresári užívateľa ''%s''?" +"v domovskom adresári používateľa ''%s''?" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Gramps verzia" +msgstr "Asistent Gramps" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" msgstr "" +"AI asistent na získavanie informácií o vašom rodinnom strome v programe " +"Gramps" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15826,29 +15826,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Si užitočný genealogický asistent s prístupom k databáze používateľa v " +"programe Gramps. Odpovedaj na otázky týkajúce sa osôb, rodín, udalostí a " +"vzťahov. Keď potrebuješ informácie z databázy, využi k tomu určené nástroje " +"– nikdy nepíš kód, nesimuluj výsledky ani si nevymýšľaj údaje. Ak pre " +"požadované informácie neexistuje žiadny nástroj, jasne to povedz." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Dodatočné nastavenia štýlu:" +msgstr "Nastavenia asistenta Gramps" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Vymazať konverzáciu a kontext" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Gramps verzia" +msgstr "Asistent Gramps:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Opýtajte sa ma čokoľvek o programe Gramps alebo o vašom konkrétnom rodinnom " +"strome v Gramps. Použite tlačidlo ⚙ na konfiguráciu umelej inteligencie (AI)" +".\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15856,10 +15860,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Nie je nakonfigurovaný žiadny model. Pred začatím konverzácie kliknite na " +"tlačidlo Nastavenia a vyberte backend a model.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Premýšľanie..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15868,95 +15875,95 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"Chyba API kľúča: premenná prostredia {var} nie je nastavená alebo je " +"neplatná. Nastavte ju pred spustením Gramps:\n" +" export {var}=váš-kľúč-je-tu" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"Chyba API kľúča: tento poskytovateľ vyžaduje API kľúč. Otvorte Nastavenia a " +"zadajte názov premennej prostredia pre váš API kľúč (napr. OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Hotovo!\n" +msgstr "Hotovo.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Systémová výzva:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Zjednodušiť nástroje (odporúčané pre menšie/lokálne modely)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Ak je táto možnosť zapnutá, do modelu sa odosielajú len nástroje relevantné " +"pre vašu otázku. Tým sa zlepšuje výkon pri menších lokálnych modeloch." #: GrampsAssistant/grampsassistant.py:1230 -#, fuzzy -#| msgid "Mistral Model" msgid "Use Local Model" -msgstr "Model Mistral" +msgstr "Použiť lokálny model" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"URL lokálneho servera kompatibilného s OpenAI. Ollama: http://localhost:11434" +" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "názov modelu (v prípade LM Studio / llama.cpp nechajte prázdne)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Model, ktorý sa má vyžiadať z lokálneho servera. Povinné pre Ollama (napr. " +"llama3.1). V prípade LM Studio alebo llama.cpp, ktoré používajú akýkoľvek " +"načítaný model, nechajte prázdne." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Moderný" +msgstr "Model:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Dátum založenia:" +msgstr "Použiť základný model" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "napr. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "Názov premennej prostredia obsahujúcej váš API kľúč." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "Premenná prostredia kľúča API:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Backend:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "URL webového sídla" +msgstr "Základná URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Meno manžela/manželky:" +msgstr "Názov modelu:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -18452,32 +18459,31 @@ msgstr "Kto (vy)hĺadá.koho?" #: NameSuite/.venv/lib/python3.12/site-packages/mypy/main.py:450 #, python-format msgid "%(prog)s: error: %(message)s\n" -msgstr "" +msgstr "%(prog)s: chyba: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Given and Patronymic Names" -msgstr "Patronymické mená:" +msgstr "Kontrola daných (krstných) mien a patronymických mien" #: NameSuite/name_processor.gpr.py:9 msgid "" "Tools to rename given name, audit and infer patronymic (East Slavic) names." msgstr "" +"Nástroje na premenovanie daného (krstného) mena, kontrolu a odvodzovanie " +"patronymických (východoslovanských) mien." #: NameSuite/name_processor.gpr.py:25 NameSuite/name_processor.gpr.py:37 -#, fuzzy -#| msgid "Patronymic names:" msgid "Patronymic Suggestion" -msgstr "Patronymické mená:" +msgstr "Návrh patronymického mena" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." msgstr "" +"Navrhuje (východoslovanské) patronymické mená v reálnom čase počas navigácie." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" -msgstr "" +msgstr "Použiť" #: NameSuite/name_processor/views/gramplet.py:32 #, python-brace-format @@ -18486,238 +18492,185 @@ msgid "" "Suggested: {0}\n" "Based on father: {1}" msgstr "" +"Zistené chýbajúce patronymické meno.\n" +"Navrhované: {0}\n" +"Na základe otca: {1}" #: NameSuite/name_processor/views/gramplet.py:34 msgid "Navigate to an individual to check patronymic status." -msgstr "" +msgstr "Prejdite na konkrétnu osobu a skontrolujte stav otcovského mena." #: NameSuite/name_processor/views/gramplet.py:35 -#, fuzzy -#| msgid "No Active Person set." msgid "No active person selected." -msgstr "Nie je nastavená aktívna osoba." +msgstr "Nebola vybraná žiadna aktívna osoba." #: NameSuite/name_processor/views/gramplet.py:37 msgid "" "Patronymic inference can't be inferred for non-binary or unknown genders." -msgstr "" +msgstr "Otcovské meno nemožno odvodiť pre nebinárne alebo neznáme pohlavia." #: NameSuite/name_processor/views/gramplet.py:40 msgid "Individual already has a recorded patronymic." -msgstr "" +msgstr "Osoba už má zaznamenané otcovské meno." #: NameSuite/name_processor/views/gramplet.py:43 msgid "No attached father found in database family records." -msgstr "" +msgstr "V databáze rodinných záznamov nebol nájdený žiadny priradený otec." #: NameSuite/name_processor/views/gramplet.py:46 msgid "Father lacks a recorded first name." -msgstr "" +msgstr "Otec nemá zaznamenané prvé (krstné) meno." #: NameSuite/name_processor/views/gramplet.py:49 msgid "Could not generate valid morphology patterns." -msgstr "" +msgstr "Nepodarilo sa vygenerovať platné morfologické vzory." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "" +msgstr "Otcovské meno bolo úspešne priradené!" #: NameSuite/name_processor/views/gramplet.py:77 -#, fuzzy -#| msgid "🔍 AI Suggestions" msgid "Apply Suggestion" -msgstr "🔍 Návrhy AI" +msgstr "Aplikovať návrh" #: NameSuite/name_processor/views/tool.py:92 msgid "Infer East Slavic Patronymics" -msgstr "" +msgstr "Odvodiť východoslovanské patronymá" #: NameSuite/name_processor/views/tool.py:112 -#, fuzzy -#| msgid "Checking Given Names" msgid "Rename Given Names" -msgstr "Kontrola daných mien" +msgstr "Premenovať dané (krstné) mená" #: NameSuite/name_processor/views/tool.py:115 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Patronymics" -msgstr "Patronymické mená:" +msgstr "Skontrolovať patronymá" #: NameSuite/name_processor/views/tool_audit_tab.py:68 msgid "Auditing Settings" -msgstr "" +msgstr "Nastavenia kontroly" #: NameSuite/name_processor/views/tool_audit_tab.py:76 -#, fuzzy -#| msgid "Place of Record" msgid "All Records" -msgstr "Miesto zápisu" +msgstr "Všetky záznamy" #: NameSuite/name_processor/views/tool_audit_tab.py:77 -#, fuzzy -#| msgid "Male line" msgid "Males Only" -msgstr "Mužská línia" +msgstr "Iba mužov" #: NameSuite/name_processor/views/tool_audit_tab.py:78 -#, fuzzy -#| msgid "Female line" msgid "Females Only" -msgstr "Ženská línia" +msgstr "Iba ženy" #: NameSuite/name_processor/views/tool_audit_tab.py:82 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules..." -msgstr "Konfigurovať" +msgstr "Konfigurácia pravidiel..." #: NameSuite/name_processor/views/tool_audit_tab.py:87 msgid "Match Pre-Revolutionary Orthography" -msgstr "" +msgstr "Zhoda s predrevolučnou ortografiou" #: NameSuite/name_processor/views/tool_audit_tab.py:95 -#, fuzzy -#| msgid "Edit tags" msgid "Audit Database" -msgstr "Upraviť štítky" +msgstr "Skontrolovať databázu" #: NameSuite/name_processor/views/tool_audit_tab.py:117 -#, fuzzy -#| msgid "Select the graph direction." msgid "Select All Safe Corrections" -msgstr "Vyberte smer diagramu." +msgstr "Vybrať všetky bezpečné opravy" #: NameSuite/name_processor/views/tool_audit_tab.py:122 #: NameSuite/name_processor/views/tool_rename_tab.py:116 -#, fuzzy -#| msgid "Apply to selected places" msgid "Apply Selected Corrections" -msgstr "Aplikovať na vybrané miesta" +msgstr "Aplikovať vybrané opravy" #: NameSuite/name_processor/views/tool_audit_tab.py:162 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules" -msgstr "Konfigurovať" +msgstr "Konfigurácia pravidiel" #: NameSuite/name_processor/views/tool_audit_tab.py:186 #: NameSuite/name_processor/views/tool_rename_tab.py:156 -#, fuzzy -#| msgid "India" msgid "Individual" -msgstr "India" +msgstr "Jedinec" #: NameSuite/name_processor/views/tool_audit_tab.py:190 #: NameSuite/name_processor/views/tool_rename_tab.py:158 -#, fuzzy -#| msgid "Current sort" msgid "Current" -msgstr "Aktuálne zoradenie" +msgstr "Aktuálne" #: NameSuite/name_processor/views/tool_audit_tab.py:193 -#, fuzzy -#| msgid "Section" msgid "Correction" -msgstr "Sekcia" +msgstr "Korekcia" #: NameSuite/name_processor/views/tool_audit_tab.py:200 -#, fuzzy -#| msgid "Configure" msgid "Conf" msgstr "Konfigurovať" #: NameSuite/name_processor/views/tool_audit_tab.py:201 -#, fuzzy -#| msgid "Event Year" msgid "Ref Year" -msgstr "Rok udalosti" +msgstr "Referenčný rok" #: NameSuite/name_processor/views/tool_audit_tab.py:204 -#, fuzzy -#| msgid "Relation" msgid "Explanation" -msgstr "Vzťah" +msgstr "Vysvetlenie" #: NameSuite/name_processor/views/tool_audit_tab.py:243 -#, fuzzy -#| msgid "Completed?" msgid "Audit Complete!" -msgstr "Dokončený?" +msgstr "Kontrola dokončená!" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "Result" msgid "No Results" -msgstr "Výsledok" +msgstr "Žiadne výsledky" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "No persons found..." msgid "No issues found." -msgstr "Žiadne osoby nenájdené..." +msgstr "Neboli nájdené žiadne problémy." #: NameSuite/name_processor/views/tool_rename_tab.py:63 -#, fuzzy -#| msgid "Search result places" msgid "Search and Replace Options" -msgstr "Výsledky vyhľadávania miest" +msgstr "Možnosti vyhľadávania a nahradzovania" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Spouse name:" msgid "Source Name:" -msgstr "Meno manžela/manželky:" +msgstr "Zdrojové meno:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" -msgstr "" +msgstr "napr. Иоанн" #: NameSuite/name_processor/views/tool_rename_tab.py:75 -#, fuzzy -#| msgid "Street Name" msgid "Target Name:" -msgstr "Názov ulice" +msgstr "Cieľové meno:" #: NameSuite/name_processor/views/tool_rename_tab.py:77 msgid "e.g. Иван" -msgstr "" +msgstr "napr. Иван" #: NameSuite/name_processor/views/tool_rename_tab.py:80 msgid "Match Mode:" -msgstr "" +msgstr "Režim zhody:" #: NameSuite/name_processor/views/tool_rename_tab.py:82 msgid "Exact Match" -msgstr "" +msgstr "Presná zhoda" #: NameSuite/name_processor/views/tool_rename_tab.py:83 -#, fuzzy -#| msgid "Search substring..." msgid "Substring" -msgstr "Hľadanie podreťazca..." +msgstr "Časť reťazca" #: NameSuite/name_processor/views/tool_rename_tab.py:84 -#, fuzzy -#| msgid "Allow regular expressions." msgid "Regular Expression" -msgstr "Povoliť regulárne výrazy." +msgstr "Regulárny výraz" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Index of Names" msgid "Scan for Names" -msgstr "Register mien" +msgstr "Skenovať mená" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" -msgstr "" +msgstr "Zachovať pôvodné meno ako alternatívu" #: NameSuite/name_processor/views/tool_rename_tab.py:160 -#, fuzzy -#| msgid "Proposed sort" msgid "Proposed" -msgstr "Navrhované zoradenie" +msgstr "Navrhované" #: NetworkChart/NetworkChart.gpr.py:24 msgid "Network Chart" @@ -23492,11 +23445,15 @@ msgid "" msgstr "" "Priame importovanie zo súborov TMG nie je podporované\n" "doplnkom TMG Importer.\n" +"\n" "Musíte použiť záložnú kópiu projektu TMG (*.sqz)\n" +"\n" "Uistite sa, že váš projekt TMG bol vytvorený pomocou:\n" "TMG verzie 5.x alebo vyššej.\n" +"\n" "Váš súbor:\n" "*.VER - Version Control File for TMG 1.2 and earlier\n" +"\n" "Ďalšie informácie nájdete na:\n" "%(gramps_wiki_import_pjc_direct_url)s" From f181b9800f30f1f826944d08cac26dba6184dee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Osman=20=C3=96z?= Date: Wed, 5 Aug 2026 20:02:08 +0200 Subject: [PATCH 097/156] Translated using Weblate (Turkish) Currently translated at 99.4% (5516 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 94.8% (5262 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 89.5% (4969 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 89.2% (4954 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 89.0% (4942 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 82.5% (4581 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 78.0% (4331 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 75.7% (4205 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 68.4% (3800 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 61.4% (3411 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 57.3% (3180 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 55.5% (3084 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 51.1% (2838 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 48.8% (2709 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ Translated using Weblate (Turkish) Currently translated at 36.0% (1998 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ --- po/tr.po | 7753 +++++++++++++++++++++++++++++------------------------- 1 file changed, 4234 insertions(+), 3519 deletions(-) diff --git a/po/tr.po b/po/tr.po index 5c204a8be..86a14960c 100644 --- a/po/tr.po +++ b/po/tr.po @@ -16,7 +16,7 @@ msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-07-03 03:01+0000\n" +"PO-Revision-Date: 2026-07-26 17:41+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish \n" @@ -25,7 +25,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" "Generated-By: pygettext.py 1.4\n" "X-Language: tr\n" "X-Source-Language: C\n" @@ -230,11 +230,6 @@ msgid "The style used for the marriage." msgstr "Evlilik için kullanılan stil." #: AncestryTableReport/AncestryTableReport.py:593 -#, fuzzy -#| msgid "" -#| "The style used for an empty row.\n" -#| "To enlarge the height of the empty row, just increase the size of the " -#| "font." msgid "" "The style used for an empty row.\n" "To enlarge the height of the empty row, just increse the size of the police." @@ -1132,9 +1127,8 @@ msgstr "" "modülü gerektirir" #: ChatWithTree/ChatWithTree.gpr.py:18 -#, fuzzy msgid "Chat With Tree" -msgstr "Chat With Tree" +msgstr "Ağaçla Sohbet Et" #: ChatWithTree/ChatWithTree.py:149 msgid "Type a message..." @@ -1274,7 +1268,7 @@ msgid "%(abbrev)s %(date)s in %(place)s" msgstr "%(abbrev)s %(date)s > %(place)s" #: CombinedView/basepage.py:495 -#, fuzzy, python-format +#, python-format msgid "%(abbrev)s %(date)s%(place)s" msgstr "%(abbrev)s %(date)s%(place)s" @@ -1318,12 +1312,12 @@ msgid "%(event_type)s: %(date)s in %(place)s" msgstr "%(event_type)s: %(date)s > %(place)s" #: CombinedView/personpage.py:601 -#, fuzzy, python-format +#, python-format msgid "%(event_type)s: %(date)s" msgstr "%(event_type)s: %(date)s" #: CombinedView/personpage.py:604 -#, fuzzy, python-format +#, python-format msgid "%(event_type)s: %(place)s" msgstr "%(event_type)s: %(place)s" @@ -1336,7 +1330,6 @@ msgid "Click to visit this link" msgstr "Bu bağlantıyı ziyaret etmek için tıklayın" #: CombinedView/personpage.py:965 CombinedView/personpage.py:972 -#, fuzzy msgid ": " msgstr ": " @@ -1643,7 +1636,6 @@ msgid "No file extension." msgstr "Dosya uzantısı yok." #: D3Charts/DescendantIndentedTree.py:1931 -#, fuzzy msgid ".html" msgstr ".html" @@ -1652,17 +1644,14 @@ msgid ".htm" msgstr ".htm" #: D3Charts/DescendantIndentedTree.py:1933 -#, fuzzy msgid ".shtml" msgstr ".shtml" #: D3Charts/DescendantIndentedTree.py:1934 -#, fuzzy msgid ".php" msgstr ".php" #: D3Charts/DescendantIndentedTree.py:1935 -#, fuzzy msgid ".php3" msgstr ".php3" @@ -1782,7 +1771,6 @@ msgstr "Bielefeld Akademik Arama" #: DEWebConnectPack/DEWebPack.py:39 SVWebconnectPack/SVWebPack.py:34 #: UKWebConnectPack/UKWebPack.py:35 USWebConnectPack/USWebPack.py:34 #: USWebConnectPack/USWebPack.py:46 -#, fuzzy msgid "FamilySearch.org" msgstr "FamilySearch.org" @@ -1791,7 +1779,6 @@ msgid "Google Archives" msgstr "Google Arşivleri" #: DEWebConnectPack/DEWebPack.py:43 -#, fuzzy msgid "DE Google" msgstr "DE Google" @@ -1864,7 +1851,7 @@ msgid " and ends at " msgstr " ve bitiş noktası " #: DNA/dnasegmentmap.py:1079 DNA/dnasegmentmap.py:1082 -#, fuzzy, python-brace-format +#, python-brace-format msgid "{0}" msgstr "{0}" @@ -1909,7 +1896,7 @@ msgstr "En Büyük Segment" #: DNAMatches/dnamatches.py:153 msgid "Pers." -msgstr "Birey" +msgstr "Kişi" #: DNAMatches/dnamatches.py:153 msgid "Rel." @@ -1956,7 +1943,6 @@ msgid "LEGEND" msgstr "KİTABE" #: DNAMatches/dnamatches.py:228 -#, fuzzy msgid "=" msgstr "=" @@ -1966,7 +1952,6 @@ msgid "Not specified" msgstr "Belirtilmemiş" #: DNAMatches/dnamatches.py:366 DNAMatches/dnamatches.py:369 -#, fuzzy msgid "cM" msgstr "cM" @@ -2060,7 +2045,7 @@ msgstr "Aktif Veriyi Kopyala" #: DataEntryGramplet/DataEntryGramplet.py:315 DenominoViso/DenominoViso.py:2002 #: Form/form_us.xml.h:2259 msgid "in" -msgstr "" +msgstr "içinde" #: DataEntryGramplet/DataEntryGramplet.py:428 #: DataEntryGramplet/DataEntryGramplet.py:507 @@ -6521,630 +6506,632 @@ msgstr "Geçersiz Form tanım dosyası" #: Form/form_ca.xml.h:661 Form/form_ca.xml.h:754 Form/form_ca.xml.h:794 #: Form/form_us.xml.h:1633 Form/form_us.xml.h:1648 msgid "Schedule" -msgstr "" +msgstr "Zamanlama" #: Form/form_ca.xml.h:2 Form/form_ca.xml.h:93 Form/form_ca.xml.h:211 #: Form/form_ca.xml.h:798 msgid "EnumDistrict" -msgstr "" +msgstr "Sayım Bölgesi" #: Form/form_ca.xml.h:3 Form/form_ca.xml.h:94 Form/form_ca.xml.h:212 #: Form/form_ca.xml.h:644 Form/form_ca.xml.h:812 Form/form_us.xml.h:1250 #: Form/form_us.xml.h:1311 Form/form_us.xml.h:2041 msgid "Township" -msgstr "" +msgstr "İlçe" #: Form/form_ca.xml.h:5 msgid "Comprising" -msgstr "" +msgstr "İçeren" #: Form/form_ca.xml.h:6 msgid "OfTheSaid" -msgstr "" +msgstr "Söz Konusu" #: Form/form_ca.xml.h:7 Form/form_ca.xml.h:293 Form/form_ca.xml.h:347 #: Form/form_ca.xml.h:396 Form/form_ca.xml.h:457 Form/form_ca.xml.h:536 #: Form/form_ca.xml.h:621 Form/form_ca.xml.h:668 Form/form_ca.xml.h:758 #: Form/form_ca.xml.h:801 msgid "Enumerator" -msgstr "" +msgstr "Nüfus Sayımı Görevlisi" #: Form/form_ca.xml.h:11 Form/form_ca.xml.h:217 msgid "1. Names of Inmates" -msgstr "" +msgstr "1. Sakinlerin Adları" #: Form/form_ca.xml.h:13 msgid "2. Profession, Trade or Occupation" -msgstr "" +msgstr "2. Meslek, Ticaret veya İş" #: Form/form_ca.xml.h:14 Form/form_ca.xml.h:220 Form/form_ca.xml.h:316 #: Form/form_ca.xml.h:370 Form/form_ca.xml.h:419 Form/form_ca.xml.h:480 #: Form/form_ca.xml.h:636 Form/form_ca.xml.h:691 Form/form_ca.xml.h:777 #: Form/form_ca.xml.h:828 msgid "BirthPlace" -msgstr "" +msgstr "Doğum Yeri" #: Form/form_ca.xml.h:15 Form/form_ca.xml.h:221 msgid "3. Place of Birth" -msgstr "" +msgstr "3. Doğum Yeri" #: Form/form_ca.xml.h:17 msgid "4. Religion" -msgstr "" +msgstr "4. Din" #: Form/form_ca.xml.h:19 msgid "5. Residence if Outside of Limits" -msgstr "" +msgstr "5. Sınırlar Dışında İkamet Ediliyorsa" #: Form/form_ca.xml.h:21 msgid "6. Age at Next Birthday" -msgstr "" +msgstr "6. Bir Sonraki Doğum Günündeki Yaş" #: Form/form_ca.xml.h:22 Form/form_ca.xml.h:230 msgid "SexMale" -msgstr "" +msgstr "Cinsiyet Erkek" #: Form/form_ca.xml.h:23 msgid "7. Sex: Male" -msgstr "" +msgstr "7. Cinsiyet: Erkek" #: Form/form_ca.xml.h:24 msgid "SexFemale" -msgstr "" +msgstr "Cinsiyet Kadın" #: Form/form_ca.xml.h:25 msgid "8. Sex: Female" -msgstr "" +msgstr "8. Cinsiyet: Kadın" #: Form/form_ca.xml.h:26 Form/form_ca.xml.h:234 Form/form_ca.xml.h:324 #: Form/form_ca.xml.h:378 Form/form_ca.xml.h:415 Form/form_ca.xml.h:472 #: Form/form_ca.xml.h:632 Form/form_ca.xml.h:683 Form/form_ca.xml.h:771 #: Form/form_ca.xml.h:824 msgid "MaritalStatus" -msgstr "" +msgstr "Medeni Durum" #: Form/form_ca.xml.h:27 msgid "9. Married or Single" -msgstr "" +msgstr "9. Evli veya Bekâr" #: Form/form_ca.xml.h:28 Form/form_ca.xml.h:240 msgid "Coloured" -msgstr "" +msgstr "Renkli" #: Form/form_ca.xml.h:29 msgid "10. Coloured Persons - Negroes" -msgstr "" +msgstr "10. Renkli Kişiler - Siyahiler" #: Form/form_ca.xml.h:30 msgid "Indians" -msgstr "" +msgstr "Hintliler" #: Form/form_ca.xml.h:31 msgid "11. Indians, if any" -msgstr "" +msgstr "11. Varsa Hintliler" #: Form/form_ca.xml.h:32 msgid "ResidentsMale" -msgstr "" +msgstr "İkamet Eden Erkek" #: Form/form_ca.xml.h:33 msgid "12. Residents: Members, Male" -msgstr "" +msgstr "12. İkamet Edenler: Üyeler, Erkek" #: Form/form_ca.xml.h:34 msgid "ResidentsFemale" -msgstr "" +msgstr "İkamet Eden Kadın" #: Form/form_ca.xml.h:35 msgid "13. Residents: Members, Female" -msgstr "" +msgstr "13. İkamet Edenler: Üyeler, Kadın" #: Form/form_ca.xml.h:36 msgid "NonMembersMale" -msgstr "" +msgstr "Üye Olmayan Erkek" #: Form/form_ca.xml.h:37 msgid "14. Residents: Not Members: Male" -msgstr "" +msgstr "14. İkamet Edenler: Üye Olmayanlar: Erkek" #: Form/form_ca.xml.h:38 msgid "NonMembersFemale" -msgstr "" +msgstr "Üye Olmayan Kadın" #: Form/form_ca.xml.h:39 msgid "15. Residents: Not Members: Female" -msgstr "" +msgstr "15. İkamet Edenler: Üye Olmayanlar: Kadın" #: Form/form_ca.xml.h:40 msgid "AbsentMembersMale" -msgstr "" +msgstr "Bulunmayan Üye Erkek" #: Form/form_ca.xml.h:41 msgid "16. Members Absent: Male" -msgstr "" +msgstr "16. Bulunmayan Üyeler: Erkek" #: Form/form_ca.xml.h:42 msgid "AbsentMembersFemale" -msgstr "" +msgstr "Bulunmayan Üye Kadın" #: Form/form_ca.xml.h:43 msgid "17. Members Absent: Female" -msgstr "" +msgstr "17. Bulunmayan Üyeler: Kadın" #: Form/form_ca.xml.h:44 msgid "DeafDumbMale" -msgstr "" +msgstr "Sağır Dilsiz Erkek" #: Form/form_ca.xml.h:45 msgid "18. Deaf and Dumb: Male" -msgstr "" +msgstr "18. Sağır ve Dilsiz: Erkek" #: Form/form_ca.xml.h:46 msgid "DeafDumbFemale" -msgstr "" +msgstr "Sağır Dilsiz Kadın" #: Form/form_ca.xml.h:47 msgid "19. Deaf and Dumb: Female" -msgstr "" +msgstr "19. Sağır ve Dilsiz: Kadın" #: Form/form_ca.xml.h:48 msgid "BlindMale" -msgstr "" +msgstr "Kör Erkek" #: Form/form_ca.xml.h:49 msgid "20. Blind: Male" -msgstr "" +msgstr "20. Kör: Erkek" #: Form/form_ca.xml.h:50 msgid "BlindFemale" -msgstr "" +msgstr "Kör Kadın" #: Form/form_ca.xml.h:51 msgid "21. Blind: Female" -msgstr "" +msgstr "21. Kör: Kadın" #: Form/form_ca.xml.h:52 msgid "LunaticMale" -msgstr "" +msgstr "Akıl Hastası Erkek" #: Form/form_ca.xml.h:53 msgid "22. Lunatics: Male" -msgstr "" +msgstr "22. Akıl Hastaları: Erkek" #: Form/form_ca.xml.h:54 msgid "LunaticFemale" -msgstr "" +msgstr "Akıl Hastası Kadın" #: Form/form_ca.xml.h:55 msgid "23. Lunatics: Female" -msgstr "" +msgstr "23. Akıl Hastaları: Kadın" #: Form/form_ca.xml.h:56 Form/form_ca.xml.h:260 msgid "ScholarMale" -msgstr "" +msgstr "Öğrenci Erkek" #: Form/form_ca.xml.h:57 msgid "24. Attending School: Male" -msgstr "" +msgstr "24. Okula Devam Eden: Erkek" #: Form/form_ca.xml.h:58 Form/form_ca.xml.h:262 msgid "ScholarFemale" -msgstr "" +msgstr "Öğrenci Kadın" #: Form/form_ca.xml.h:59 msgid "25. Attending School: Female" -msgstr "" +msgstr "25. Okula Devam Eden: Kadın" #: Form/form_ca.xml.h:60 msgid "Birth1851Male" -msgstr "" +msgstr "1851 Doğum Erkek" #: Form/form_ca.xml.h:61 msgid "26. Births During the Year 1851: Male" -msgstr "" +msgstr "26. 1851 Yılında Doğanlar: Erkek" #: Form/form_ca.xml.h:62 msgid "Birth1851Female" -msgstr "" +msgstr "1851 Doğum Kadın" #: Form/form_ca.xml.h:63 msgid "27. Births During the Year 1851: Female" -msgstr "" +msgstr "27. 1851 Yılında Doğanlar: Kadın" #: Form/form_ca.xml.h:64 msgid "Death1851Male" -msgstr "" +msgstr "1851 Ölüm Erkek" #: Form/form_ca.xml.h:65 msgid "28. Deaths During the Year 1851: Male" -msgstr "" +msgstr "28. 1851 Yılında Ölümler: Erkek" #: Form/form_ca.xml.h:66 msgid "Death1851Female" -msgstr "" +msgstr "1851 Ölüm Kadın" #: Form/form_ca.xml.h:67 msgid "29. Deaths During the Year 1851: Female" -msgstr "" +msgstr "29. 1851 Yılında Ölümler: Kadın" #: Form/form_ca.xml.h:68 Form/form_ca.xml.h:276 msgid "DeathAgeCause" -msgstr "" +msgstr "Ölüm Yaşı Nedeni" #: Form/form_ca.xml.h:69 msgid "30. Age and Cause of Deaths" -msgstr "" +msgstr "30. Ölümlerin Yaşı ve Nedeni" #: Form/form_ca.xml.h:70 Form/form_ca.xml.h:278 msgid "Houses" -msgstr "" +msgstr "Evler" #: Form/form_ca.xml.h:71 msgid "" "31. Houses: Brick, Stone, Frame, Log, Shanty, or other kinds of residence" -msgstr "" +msgstr "31. Evler: Tuğla, Taş, Ahşap, Kütük, Baraka veya diğer ikamet türleri" #: Form/form_ca.xml.h:72 Form/form_ca.xml.h:280 msgid "HousesStories" -msgstr "" +msgstr "Evler Kat Sayısı" #: Form/form_ca.xml.h:73 msgid "32. Houses: No. of Stories" -msgstr "" +msgstr "32. Evler: Kat Sayısı" #: Form/form_ca.xml.h:74 Form/form_ca.xml.h:282 msgid "HousesFamilies" -msgstr "" +msgstr "Evler Aile Sayısı" #: Form/form_ca.xml.h:75 msgid "33. No. of Families Occupying" -msgstr "" +msgstr "33. Evde Oturan Aile Sayısı" #: Form/form_ca.xml.h:76 Form/form_ca.xml.h:284 Form/form_ca.xml.h:302 #: Form/form_ca.xml.h:356 Form/form_ca.xml.h:403 Form/form_ca.xml.h:549 msgid "HousesVacant" -msgstr "" +msgstr "Evler Boş" #: Form/form_ca.xml.h:77 msgid "34. Houses: Vacant" -msgstr "" +msgstr "34. Evler: Boş" #: Form/form_ca.xml.h:78 msgid "HousesBuilding" -msgstr "" +msgstr "Evler Yapım" #: Form/form_ca.xml.h:79 msgid "35. Houses: Building" -msgstr "" +msgstr "35. Evler: Yapım" #: Form/form_ca.xml.h:80 msgid "Shops" -msgstr "" +msgstr "Dükkanlar" #: Form/form_ca.xml.h:81 msgid "36. Shops, Stores, Inns, Taverns etc." -msgstr "" +msgstr "36. Dükkanlar, Mağazalar, Hanlar, Tavernalar vb." #: Form/form_ca.xml.h:82 msgid "PublicBuildings" -msgstr "" +msgstr "Kamu Binaları" #: Form/form_ca.xml.h:83 msgid "37. Public Buildings" -msgstr "" +msgstr "37. Kamu Binaları" #: Form/form_ca.xml.h:84 msgid "WorshipPlaces" -msgstr "" +msgstr "İbadet Yerleri" #: Form/form_ca.xml.h:85 msgid "38. Places of Worship" -msgstr "" +msgstr "38. İbadet Yerleri" #: Form/form_ca.xml.h:86 msgid "BuisnessInfo" -msgstr "" +msgstr "İşletme Bilgileri" #: Form/form_ca.xml.h:87 msgid "" "39. Information on Mills, Factories etc., their cost, power, produce, etc." msgstr "" +"39. Değirmenler, Fabrikalar vb. hakkında bilgiler; maliyetleri, güçleri, " +"üretimleri vb." #: Form/form_ca.xml.h:88 msgid "NumEmployees" -msgstr "" +msgstr "Çalışan Sayısı" #: Form/form_ca.xml.h:89 msgid "40. Number of Persons Usually Employed Therein" -msgstr "" +msgstr "40. Orada Genellikle Çalıştırılan Kişi Sayısı" #: Form/form_ca.xml.h:91 msgid "41. General Remarks of the Enumerator" -msgstr "" +msgstr "41. Sayım Görevlisinin Genel Açıklamaları" #: Form/form_ca.xml.h:99 msgid "1. Name of Occupier" -msgstr "" +msgstr "1. Oturanın Adı" #: Form/form_ca.xml.h:100 msgid "Concession" -msgstr "" +msgstr "İmtiyaz" #: Form/form_ca.xml.h:101 msgid "2. Concession or Range" -msgstr "" +msgstr "2. İmtiyaz veya Bölge" #: Form/form_ca.xml.h:102 msgid "Lot" -msgstr "" +msgstr "Parsel" #: Form/form_ca.xml.h:103 msgid "3. Lot or part of Lot" -msgstr "" +msgstr "3. Parsel veya Parselin Bir Kısmı" #: Form/form_ca.xml.h:104 msgid "AcresHeld" -msgstr "" +msgstr "Sahip Olunan Dönüm" #: Form/form_ca.xml.h:105 msgid "4. Number of Acres of Land: Held by each person or family" -msgstr "" +msgstr "4. Her kişi veya aile tarafından sahip olunan arazi dönümü sayısı" #: Form/form_ca.xml.h:106 msgid "AcresCultivation" -msgstr "" +msgstr "Ekili Dönüm" #: Form/form_ca.xml.h:107 msgid "5. Number of Acres of Land: Under Cultivation" -msgstr "" +msgstr "5. Ekili durumda olan arazi dönümü sayısı" #: Form/form_ca.xml.h:108 msgid "AcresCrops" -msgstr "" +msgstr "Ürün Dönümü" #: Form/form_ca.xml.h:109 msgid "6. Number of Acres of Land: Under Crops in 1851" -msgstr "" +msgstr "6. 1851 yılında ürün altında bulunan arazi dönümü sayısı" #: Form/form_ca.xml.h:110 msgid "AcresPasture" -msgstr "" +msgstr "Mera Dönümü" #: Form/form_ca.xml.h:111 msgid "7. Number of Acres of Land: Under Pasture in 1851" -msgstr "" +msgstr "7. 1851 yılında mera olarak kullanılan arazi dönümü sayısı" #: Form/form_ca.xml.h:112 msgid "AcresGardens" -msgstr "" +msgstr "Bahçe Dönümü" #: Form/form_ca.xml.h:113 msgid "8. Number of Acres of Land: Gardens or Orchards" -msgstr "" +msgstr "8. Bahçe veya meyve bahçesi olan arazi dönümü sayısı" #: Form/form_ca.xml.h:114 msgid "AcresWood" -msgstr "" +msgstr "Orman Dönümü" #: Form/form_ca.xml.h:115 msgid "9. Number of Acres of Land: Under Wood or Wild" -msgstr "" +msgstr "9. Ormanlık veya yabani durumda olan arazi dönümü sayısı" #: Form/form_ca.xml.h:116 msgid "WheatAcres" -msgstr "" +msgstr "Buğday Dönümü" #: Form/form_ca.xml.h:117 msgid "10. Wheat: Acres" -msgstr "" +msgstr "10. Buğday: Dönüm" #: Form/form_ca.xml.h:118 msgid "WheatBsh" -msgstr "" +msgstr "Buğday Kilesi" #: Form/form_ca.xml.h:119 msgid "11. Wheat: Produce Bsh." -msgstr "" +msgstr "11. Buğday: Üretim Kilesi." #: Form/form_ca.xml.h:120 msgid "BarleyAcres" -msgstr "" +msgstr "Arpa Dönüm" #: Form/form_ca.xml.h:121 msgid "12. Barley: Acres" -msgstr "" +msgstr "12. Arpa: Dönüm" #: Form/form_ca.xml.h:122 msgid "BarleyBsh" -msgstr "" +msgstr "Arpa Kile" #: Form/form_ca.xml.h:123 msgid "13. Barley: Produce Bsh." -msgstr "" +msgstr "13. Arpa: Üretim Kilesi." #: Form/form_ca.xml.h:124 msgid "RyeAcres" -msgstr "" +msgstr "Çavdar Dönüm" #: Form/form_ca.xml.h:125 msgid "14. Rye: Acres" -msgstr "" +msgstr "14. Çavdar: Dönüm" #: Form/form_ca.xml.h:126 msgid "RyeBsh" -msgstr "" +msgstr "Çavdar Kile" #: Form/form_ca.xml.h:127 msgid "15. Rye: Produce Bsh." -msgstr "" +msgstr "15. Çavdar: Üretim Kilesi." #: Form/form_ca.xml.h:128 msgid "PeasAcres" -msgstr "" +msgstr "Bezelye Dönüm" #: Form/form_ca.xml.h:129 msgid "16. Peas: Acres" -msgstr "" +msgstr "16. Bezelye: Dönüm" #: Form/form_ca.xml.h:130 msgid "PeasBush" -msgstr "" +msgstr "Bezelye Kile" #: Form/form_ca.xml.h:131 msgid "17. Peas: Produce Bsh." -msgstr "" +msgstr "17. Bezelye: Üretim Kilesi." #: Form/form_ca.xml.h:132 msgid "OatsAcres" -msgstr "" +msgstr "Yulaf Dönüm" #: Form/form_ca.xml.h:133 msgid "18. Oats: Acres" -msgstr "" +msgstr "18. Yulaf: Dönüm" #: Form/form_ca.xml.h:134 msgid "OatsBush" -msgstr "" +msgstr "Yulaf Kile" #: Form/form_ca.xml.h:135 msgid "19. Oats: Produce Bsh." -msgstr "" +msgstr "19. Yulaf:Üretim Kilesi." #: Form/form_ca.xml.h:136 msgid "BWheatAcres" -msgstr "" +msgstr "Karabuğday Dönüm" #: Form/form_ca.xml.h:137 msgid "20. B.Wheat: Acres" -msgstr "" +msgstr "20. Karabuğday: Dönüm" #: Form/form_ca.xml.h:138 msgid "BWheatBush" -msgstr "" +msgstr "Karabuğday Kile" #: Form/form_ca.xml.h:139 msgid "21. B.Wheat: Produce Bsh." -msgstr "" +msgstr "21. Karabuğday: Üretim Kilesi." #: Form/form_ca.xml.h:140 msgid "CornAcres" -msgstr "" +msgstr "Mısır Dönüm" #: Form/form_ca.xml.h:141 msgid "22. Corn: Acres" -msgstr "" +msgstr "22. Mısır: Dönüm" #: Form/form_ca.xml.h:142 msgid "CornBush" -msgstr "" +msgstr "Mısır Kile" #: Form/form_ca.xml.h:143 msgid "23. Corn: Produce Bsh." -msgstr "" +msgstr "23. Mısır: Üretim Kilesi." #: Form/form_ca.xml.h:144 msgid "PotatoesAcres" -msgstr "" +msgstr "Patates Dönüm" #: Form/form_ca.xml.h:145 msgid "24. Potatoes: Acres" -msgstr "" +msgstr "24. Patates: Dönüm" #: Form/form_ca.xml.h:146 msgid "PotatoesBush" -msgstr "" +msgstr "Patates Kile" #: Form/form_ca.xml.h:147 msgid "25. Potatoes: Produce Bsh." -msgstr "" +msgstr "25. Patates: Üretim Kilesi." #: Form/form_ca.xml.h:148 msgid "TurnipsAcres" -msgstr "" +msgstr "Şalgam Dönüm" #: Form/form_ca.xml.h:149 msgid "26. Turnips: Acres" -msgstr "" +msgstr "26. Şalgam: Dönüm" #: Form/form_ca.xml.h:150 msgid "TurnipsBush" -msgstr "" +msgstr "Şalgam Kile" #: Form/form_ca.xml.h:151 msgid "27. Turnips: Produce Bsh." -msgstr "" +msgstr "27. Şalgam: Üretim Kilesi." #: Form/form_ca.xml.h:152 msgid "CloverBush" -msgstr "" +msgstr "Yonca Kile" #: Form/form_ca.xml.h:153 msgid "28. Clover, Timothy or other grass seed - Bsh." -msgstr "" +msgstr "28. Yonca, Çayır veya Diğer Çim Tohumu - Kilesi." #: Form/form_ca.xml.h:154 msgid "CarrotsBush" -msgstr "" +msgstr "Havuç Kile" #: Form/form_ca.xml.h:155 msgid "29. Carrots - Bsh." -msgstr "" +msgstr "29. Havuç - Kilesi." #: Form/form_ca.xml.h:156 msgid "MangleWurtzel" -msgstr "" +msgstr "Pancar" #: Form/form_ca.xml.h:157 msgid "30. Mangle Wurtzel" -msgstr "" +msgstr "30. Pancar" #: Form/form_ca.xml.h:158 msgid "BeansBush" -msgstr "" +msgstr "Fasulye Kile" #: Form/form_ca.xml.h:159 msgid "31. Beans Bush" -msgstr "" +msgstr "31. Fasulye Kilesi" #: Form/form_ca.xml.h:160 msgid "HopsLbs" -msgstr "" +msgstr "Şerbetçiotu Kilosu" #: Form/form_ca.xml.h:161 msgid "32. Hops Lbs." -msgstr "" +msgstr "32. Şerbetçiotu Kilosu." #: Form/form_ca.xml.h:162 msgid "HayQty" -msgstr "" +msgstr "Saman Miktarı" #: Form/form_ca.xml.h:163 msgid "33. Hay, Bundles or Tons" -msgstr "" +msgstr "33. Saman, Demet veya Ton" #: Form/form_ca.xml.h:164 msgid "FlaxLbs" -msgstr "" +msgstr "Keten Kilosu" #: Form/form_ca.xml.h:165 msgid "34. Flax or Hemp, Lbs." -msgstr "" +msgstr "34. Keten veya Kenevir, Kilosu." #: Form/form_ca.xml.h:166 msgid "TobaccoLbs" -msgstr "" +msgstr "Tütün Kilosu" #: Form/form_ca.xml.h:167 msgid "35. Tobacco Lbs." -msgstr "" +msgstr "35. Tütün Kilosu." #: Form/form_ca.xml.h:168 msgid "WoolLbs." -msgstr "" +msgstr "Yün Kilosu." #: Form/form_ca.xml.h:169 msgid "36. Wool Lbs." -msgstr "" +msgstr "36. Yün Kilosu." #: Form/form_ca.xml.h:170 msgid "Column37" @@ -7152,7 +7139,7 @@ msgstr "Sütun37" #: Form/form_ca.xml.h:171 msgid "37." -msgstr "" +msgstr "37." #: Form/form_ca.xml.h:172 msgid "Column38" @@ -7160,7 +7147,7 @@ msgstr "Sütun38" #: Form/form_ca.xml.h:173 msgid "38." -msgstr "" +msgstr "38." #: Form/form_ca.xml.h:174 msgid "Column39" @@ -7168,258 +7155,258 @@ msgstr "Sütun39" #: Form/form_ca.xml.h:175 msgid "39." -msgstr "" +msgstr "39." #: Form/form_ca.xml.h:176 msgid "MapleSugarLbs" -msgstr "" +msgstr "Akçaağaç Şekeri Kilosu" #: Form/form_ca.xml.h:177 msgid "40. Maple Sugar Lbs." -msgstr "" +msgstr "40. Akçaağaç Şekeri Kilosu." #: Form/form_ca.xml.h:178 msgid "CiderGalls" -msgstr "" +msgstr "Elma Suyu Galonu" #: Form/form_ca.xml.h:179 msgid "41. Cider Galls." -msgstr "" +msgstr "41. Elma Suyu Galonu." #: Form/form_ca.xml.h:180 msgid "ClothYards" -msgstr "" +msgstr "Kumaş Metreleri" #: Form/form_ca.xml.h:181 msgid "42. Fulled Cloth Yards" -msgstr "" +msgstr "42. Keçe Kumaş Metreleri" #: Form/form_ca.xml.h:182 msgid "LinnenYards" -msgstr "" +msgstr "Keten Metreleri" #: Form/form_ca.xml.h:183 msgid "43. Linnen - Yds." -msgstr "" +msgstr "43. Keten - Metreleri." #: Form/form_ca.xml.h:184 msgid "FlannelYds" -msgstr "" +msgstr "Pazen Metreleri" #: Form/form_ca.xml.h:185 msgid "44. Flannel - Yds." -msgstr "" +msgstr "44. Pazen - Metreleri." #: Form/form_ca.xml.h:186 msgid "Bulls" -msgstr "" +msgstr "Boğalar" #: Form/form_ca.xml.h:187 msgid "45. Bulls, Oxen or Steers" -msgstr "" +msgstr "45. Boğalar, Öküzler veya Tosunlar" #: Form/form_ca.xml.h:188 Form/form_us.xml.h:288 Form/form_us.xml.h:366 msgid "Milch Cows" -msgstr "" +msgstr "Süt İnekleri" #: Form/form_ca.xml.h:189 msgid "46. Milch Cows" -msgstr "" +msgstr "46. Süt İnekleri" #: Form/form_ca.xml.h:190 msgid "Calves" -msgstr "" +msgstr "Buzağılar" #: Form/form_ca.xml.h:191 msgid "47. Calves or Heifers" -msgstr "" +msgstr "47. Buzağılar veya Düveler" #: Form/form_ca.xml.h:192 Form/form_ca.xml.h:650 Form/form_us.xml.h:286 #: Form/form_us.xml.h:364 msgid "Horses" -msgstr "" +msgstr "Atlar" #: Form/form_ca.xml.h:193 msgid "48. Horses of all ages" -msgstr "" +msgstr "48. Her Yaştan Atlar" #: Form/form_ca.xml.h:194 Form/form_ca.xml.h:656 Form/form_us.xml.h:291 #: Form/form_us.xml.h:369 msgid "Sheep" -msgstr "" +msgstr "Koyunlar" #: Form/form_ca.xml.h:195 msgid "49. Sheep" -msgstr "" +msgstr "49. Koyunlar" #: Form/form_ca.xml.h:196 Form/form_ca.xml.h:658 msgid "Pigs" -msgstr "" +msgstr "Domuzlar" #: Form/form_ca.xml.h:197 msgid "50. Pigs" -msgstr "" +msgstr "50. Domuzlar" #: Form/form_ca.xml.h:198 msgid "ButterLbs" -msgstr "" +msgstr "Tereyağı Kilosu" #: Form/form_ca.xml.h:199 msgid "51. Butter - Lbs." -msgstr "" +msgstr "51. Tereyağı - Kilosu." #: Form/form_ca.xml.h:200 msgid "CheeseLbs" -msgstr "" +msgstr "Peynir Kilosu" #: Form/form_ca.xml.h:201 msgid "52. Cheese - Lbs." -msgstr "" +msgstr "52. Peynir - Kilosu." #: Form/form_ca.xml.h:202 msgid "Beef" -msgstr "" +msgstr "Sığır Eti" #: Form/form_ca.xml.h:203 msgid "53. Beef - Barrels or Cwts." -msgstr "" +msgstr "53. Sığır Eti - Variller veya Tonu." #: Form/form_ca.xml.h:204 msgid "Pork" -msgstr "" +msgstr "Domuz Eti" #: Form/form_ca.xml.h:205 msgid "54. Pork - Barrels or Cwts." -msgstr "" +msgstr "54. Domuz Eti - Variller veya Tonu." #: Form/form_ca.xml.h:206 msgid "Fish" -msgstr "" +msgstr "Balık" #: Form/form_ca.xml.h:207 msgid "55. Quantity of Fish Cured" -msgstr "" +msgstr "55. İşlenmiş Balık Miktarı" #: Form/form_ca.xml.h:209 msgid "56. Remarks" -msgstr "" +msgstr "56. Açıklamalar" #: Form/form_ca.xml.h:219 msgid "2. Profession, trade or occupation" -msgstr "" +msgstr "2. Meslek, ticaret veya iş" #: Form/form_ca.xml.h:222 msgid "Married1851" -msgstr "" +msgstr "Evlilik 1851" #: Form/form_ca.xml.h:223 msgid "4. Married during the year" -msgstr "" +msgstr "4. Yıl içinde evlenenler" #: Form/form_ca.xml.h:225 msgid "5. Religion" -msgstr "" +msgstr "5. Din" #: Form/form_ca.xml.h:227 msgid "6. Residence, if out of limits" -msgstr "" +msgstr "6. İkamet yeri, sınırlar dışındaysa" #: Form/form_ca.xml.h:229 msgid "7. Age next Birthday" -msgstr "" +msgstr "7. Bir sonraki doğum günündeki yaş" #: Form/form_ca.xml.h:231 msgid "8. Sex: Male" -msgstr "" +msgstr "8. Cinsiyet: Erkek" #: Form/form_ca.xml.h:232 msgid "SexFemail" -msgstr "" +msgstr "Cinsiyet Kadın" #: Form/form_ca.xml.h:233 msgid "9. Sex:Female" -msgstr "" +msgstr "9. Cinsiyet: Kadın" #: Form/form_ca.xml.h:235 msgid "10. Married or Single" -msgstr "" +msgstr "10. Evli veya Bekâr" #: Form/form_ca.xml.h:236 msgid "Widowers" -msgstr "" +msgstr "Dullar" #: Form/form_ca.xml.h:237 msgid "11. Widowers" -msgstr "" +msgstr "11. Dullar" #: Form/form_ca.xml.h:238 msgid "Widows" -msgstr "" +msgstr "Dullar" #: Form/form_ca.xml.h:239 msgid "12. Widows" -msgstr "" +msgstr "12. Dullar" #: Form/form_ca.xml.h:241 msgid "13. Coloured Persons, Mulato or Indians" -msgstr "" +msgstr "13. Renkli Kişiler, Melezler veya Hintliler" #: Form/form_ca.xml.h:242 msgid "MemberMale" -msgstr "" +msgstr "Üye Erkek" #: Form/form_ca.xml.h:243 msgid "14. Members: Male" -msgstr "" +msgstr "14. Üyeler: Erkek" #: Form/form_ca.xml.h:244 msgid "MemberFemale" -msgstr "" +msgstr "Üye Kadın" #: Form/form_ca.xml.h:245 msgid "15. Members: Female" -msgstr "" +msgstr "15. Üyeler: Kadın" #: Form/form_ca.xml.h:246 msgid "NotMemberMale" -msgstr "" +msgstr "Üye Olmayan Erkek" #: Form/form_ca.xml.h:247 msgid "16. Not Members: Male" -msgstr "" +msgstr "16. Üye Olmayanlar: Erkek" #: Form/form_ca.xml.h:248 msgid "NotMemberFemale" -msgstr "" +msgstr "Üye Olmayan Kadın" #: Form/form_ca.xml.h:249 msgid "17. Not Members: Female" -msgstr "" +msgstr "17. Üye Olmayanlar: Kadın" #: Form/form_ca.xml.h:250 msgid "MemberAbsentMale" -msgstr "" +msgstr "Üye Olmayan Erkek" #: Form/form_ca.xml.h:251 msgid "18. Members Absent: Male" -msgstr "" +msgstr "18. Üye Olmayanlar: Erkek" #: Form/form_ca.xml.h:252 msgid "MemberAbsentFemale" -msgstr "" +msgstr "Üye Olmayan Kadın" #: Form/form_ca.xml.h:253 msgid "19. Members Absent: Female" -msgstr "" +msgstr "19. Üye Olmayanlar: Kadın" #: Form/form_ca.xml.h:254 Form/form_ca.xml.h:334 Form/form_ca.xml.h:382 #: Form/form_ca.xml.h:443 Form/form_ca.xml.h:747 msgid "DeafDumb" -msgstr "" +msgstr "Sağır Dilsiz" #: Form/form_ca.xml.h:255 Form/form_ca.xml.h:335 msgid "20. Deaf and Dumb" -msgstr "" +msgstr "20. Sağır ve Dilsiz" #: Form/form_ca.xml.h:256 Form/form_ca.xml.h:336 Form/form_ca.xml.h:384 #: Form/form_ca.xml.h:445 Form/form_ca.xml.h:745 Form/form_us.xml.h:479 @@ -7429,155 +7416,155 @@ msgstr "" #: Form/form_us.xml.h:2181 Form/form_us.xml.h:2219 Form/form_us.xml.h:2242 #: Form/form_us.xml.h:2254 Form/form_us.xml.h:2266 msgid "Blind" -msgstr "" +msgstr "Kör" #: Form/form_ca.xml.h:257 Form/form_ca.xml.h:337 msgid "21. Blind" -msgstr "" +msgstr "21. Kör" #: Form/form_ca.xml.h:258 Form/form_ca.xml.h:338 Form/form_ca.xml.h:386 #: Form/form_ca.xml.h:447 Form/form_ca.xml.h:749 msgid "Lunatic" -msgstr "" +msgstr "Akıl Hastası" #: Form/form_ca.xml.h:259 msgid "22. Lunatics or Idiots" -msgstr "" +msgstr "22. Akıl Hastaları veya Zihinsel Engelliler" #: Form/form_ca.xml.h:261 msgid "23. Attending School within the year: Male" -msgstr "" +msgstr "23. Yıl İçinde Okula Devam Edenler: Erkek" #: Form/form_ca.xml.h:263 msgid "24. Attending School within the year: Female" -msgstr "" +msgstr "24. Yıl İçinde Okula Devam Edenler: Kadın" #: Form/form_ca.xml.h:264 msgid "CantReadMale" -msgstr "" +msgstr "Okuyamayan Erkek" #: Form/form_ca.xml.h:265 msgid "25. Persons over 20, who cannot read or write: Male" -msgstr "" +msgstr "25. 20 Yaş Üstü Okuyup Yazamayan Kişiler: Erkek" #: Form/form_ca.xml.h:266 msgid "CantReadFemale" -msgstr "" +msgstr "Okuyamayan Kadın" #: Form/form_ca.xml.h:267 msgid "26. Persons over 20, who cannot read or write: Female" -msgstr "" +msgstr "26. 20 Yaş Üstü Okuyup Yazamayan Kişiler: Kadın" #: Form/form_ca.xml.h:268 msgid "Birth1860Male" -msgstr "" +msgstr "1860 Doğumlu Erkek" #: Form/form_ca.xml.h:269 msgid "27. Births in 1860: Male" -msgstr "" +msgstr "27. 1860 Doğumlular: Erkek" #: Form/form_ca.xml.h:270 msgid "Birth1860Female" -msgstr "" +msgstr "1860 Doğumlu Kadın" #: Form/form_ca.xml.h:271 msgid "28. Births in 1860: Female" -msgstr "" +msgstr "28. 1860 Doğumlar: Kadın" #: Form/form_ca.xml.h:272 msgid "Death1860Male" -msgstr "" +msgstr "1860 Yılında Ölen Erkek" #: Form/form_ca.xml.h:273 msgid "29. Deaths in 1860: Male" -msgstr "" +msgstr "29. 1860 Yılındaki Ölümler: Erkek" #: Form/form_ca.xml.h:274 msgid "Death1860Female" -msgstr "" +msgstr "1860 Yılında Ölen Kadın" #: Form/form_ca.xml.h:275 msgid "30. Deaths in 1860: Female" -msgstr "" +msgstr "30. 1860 Yılındaki Ölümler: Kadın" #: Form/form_ca.xml.h:277 msgid "31. Age and cause of death" -msgstr "" +msgstr "31. Ölüm yaşı ve nedeni" #: Form/form_ca.xml.h:279 msgid "32. Houses: Brick, Stone, Frame, Log, etc." -msgstr "" +msgstr "32. Evler: Tuğla, Taş, Ahşap Karkas, Kütük vb." #: Form/form_ca.xml.h:281 msgid "33. Houses: No. of Stories" -msgstr "" +msgstr "33. Evler: Kat Sayısı" #: Form/form_ca.xml.h:283 msgid "34. Houses: No. of families living in the house" -msgstr "" +msgstr "34. Evler: Evde Yaşayan Aile Sayısı" #: Form/form_ca.xml.h:285 msgid "35. Houses: Vacant" -msgstr "" +msgstr "35. Evler: Boş" #: Form/form_ca.xml.h:286 msgid "HouseBeingBuilt" -msgstr "" +msgstr "Yapım Aşamasındaki Ev" #: Form/form_ca.xml.h:287 msgid "36. Houses: Being built" -msgstr "" +msgstr "36. Evler: Yapım aşamasında" #: Form/form_ca.xml.h:291 Form/form_ca.xml.h:345 Form/form_ca.xml.h:393 #: Form/form_ca.xml.h:453 Form/form_ca.xml.h:532 Form/form_ca.xml.h:618 #: Form/form_ca.xml.h:664 Form/form_ca.xml.h:797 msgid "SubDistrict" -msgstr "" +msgstr "Alt Bölge" #: Form/form_ca.xml.h:296 Form/form_ca.xml.h:350 Form/form_ca.xml.h:399 msgid "Vessels" -msgstr "" +msgstr "Tekneler" #: Form/form_ca.xml.h:297 Form/form_ca.xml.h:351 msgid "1. Vessels" -msgstr "" +msgstr "1. Tekneler" #: Form/form_ca.xml.h:298 Form/form_ca.xml.h:352 msgid "Shanties" -msgstr "" +msgstr "Barakalar" #: Form/form_ca.xml.h:299 Form/form_ca.xml.h:353 msgid "2. Shanties" -msgstr "" +msgstr "2. Barakalar" #: Form/form_ca.xml.h:300 Form/form_ca.xml.h:354 Form/form_ca.xml.h:401 msgid "HousesBeingBuilt" -msgstr "" +msgstr "Yapım Aşamasındaki Evler" #: Form/form_ca.xml.h:301 msgid "3. Dwelling houses in construction" -msgstr "" +msgstr "3. Yapım aşamasındaki evler" #: Form/form_ca.xml.h:303 msgid "4. Dwelling houses unoccupied" -msgstr "" +msgstr "4. Boş evler" #: Form/form_ca.xml.h:304 Form/form_ca.xml.h:358 Form/form_ca.xml.h:405 #: Form/form_ca.xml.h:551 msgid "HousesInhabited" -msgstr "" +msgstr "Oturulan Evler" #: Form/form_ca.xml.h:305 msgid "5. Dwelling houses inhabited" -msgstr "" +msgstr "5. Oturulan evler" #: Form/form_ca.xml.h:307 msgid "6.Families" -msgstr "" +msgstr "6. Aileler" #: Form/form_ca.xml.h:309 Form/form_ca.xml.h:363 msgid "7. Names" -msgstr "" +msgstr "7. Adlar" #: Form/form_ca.xml.h:310 Form/form_ca.xml.h:364 Form/form_ca.xml.h:411 #: Form/form_ca.xml.h:466 Form/form_ca.xml.h:630 Form/form_ca.xml.h:679 @@ -7602,159 +7589,159 @@ msgstr "" #: Form/form_us.xml.h:2333 Form/form_us.xml.h:2348 Form/form_us.xml.h:2376 #: Form/form_us.xml.h:2436 Form/form_us.xml.h:2466 Form/form_us.xml.h:2490 msgid "Sex" -msgstr "" +msgstr "Cinsiyet" #: Form/form_ca.xml.h:311 Form/form_ca.xml.h:365 msgid "8. Sex" -msgstr "" +msgstr "8. Cinsiyet" #: Form/form_ca.xml.h:313 Form/form_ca.xml.h:367 msgid "9. Age" -msgstr "" +msgstr "9. Yaş" #: Form/form_ca.xml.h:314 msgid "Birth1871" -msgstr "" +msgstr "Doğum 1871" #: Form/form_ca.xml.h:315 Form/form_ca.xml.h:369 msgid "10. Born within last twelve months" -msgstr "" +msgstr "10. Son on iki ay içinde doğmuş" #: Form/form_ca.xml.h:317 Form/form_ca.xml.h:371 Form/form_ca.xml.h:420 msgid "11. Country or Province of Birth" -msgstr "" +msgstr "11. Doğum Ülkesi veya Eyaleti" #: Form/form_ca.xml.h:319 Form/form_ca.xml.h:373 msgid "12. Religion" -msgstr "" +msgstr "12. Din" #: Form/form_ca.xml.h:321 Form/form_ca.xml.h:375 msgid "13. Origin" -msgstr "" +msgstr "13. Köken" #: Form/form_ca.xml.h:323 Form/form_ca.xml.h:377 msgid "14. Profession, Occupation or Trade" -msgstr "" +msgstr "14. Meslek, İş veya Ticaret" #: Form/form_ca.xml.h:325 Form/form_ca.xml.h:379 msgid "15. Married or Widowed" -msgstr "" +msgstr "15. Evli veya Dul" #: Form/form_ca.xml.h:326 msgid "Maried1871" -msgstr "" +msgstr "Evli 1871" #: Form/form_ca.xml.h:327 msgid "16. Married within last twelve months" -msgstr "" +msgstr "16. Son on iki ay içinde evlenmiş" #: Form/form_ca.xml.h:328 Form/form_ca.xml.h:380 msgid "Scholar" -msgstr "" +msgstr "Öğrenci" #: Form/form_ca.xml.h:329 msgid "17. Going to school" -msgstr "" +msgstr "17. Okula gidiyor" #: Form/form_ca.xml.h:330 msgid "CantRead" -msgstr "" +msgstr "Okuyamıyor" #: Form/form_ca.xml.h:331 msgid "18. Over 20 unable to read" -msgstr "" +msgstr "18. 20 yaşın üzerinde olup okuyamayan" #: Form/form_ca.xml.h:332 msgid "CantWrite" -msgstr "" +msgstr "Yazamıyor" #: Form/form_ca.xml.h:333 msgid "19. Over 20 unable to write" -msgstr "" +msgstr "19. 20 yaşın üzerinde olup yazamayan" #: Form/form_ca.xml.h:339 msgid "22. Unsound mind" -msgstr "" +msgstr "22. Akıl sağlığı yerinde değil" #: Form/form_ca.xml.h:341 msgid "23. Date of Operations and Remarks" -msgstr "" +msgstr "23. İşlemlerin Tarihi ve Açıklamalar" #: Form/form_ca.xml.h:355 msgid "3. Houses in construction" -msgstr "" +msgstr "3. Yapım aşamasındaki evler" #: Form/form_ca.xml.h:357 msgid "4. Houses unoccupied" -msgstr "" +msgstr "4. Boş evler" #: Form/form_ca.xml.h:359 msgid "5. Houses inhabited" -msgstr "" +msgstr "5. Oturulan evler" #: Form/form_ca.xml.h:361 msgid "6. Families" -msgstr "" +msgstr "6. Aileler" #: Form/form_ca.xml.h:368 msgid "Birth1881" -msgstr "" +msgstr "Doğum 1881" #: Form/form_ca.xml.h:381 msgid "16. Going to school" -msgstr "" +msgstr "16. Okula gidiyor" #: Form/form_ca.xml.h:383 msgid "17. Deaf and Dumb" -msgstr "" +msgstr "17. Sağır ve Dilsiz" #: Form/form_ca.xml.h:385 msgid "18. Blind" -msgstr "" +msgstr "18. Kör" #: Form/form_ca.xml.h:387 msgid "19. Unsound mind" -msgstr "" +msgstr "19. Akıl sağlığı yerinde değil" #: Form/form_ca.xml.h:389 msgid "20. Date of Operations and Remarks" -msgstr "" +msgstr "20. İşlemlerin Tarihi ve Açıklamalar" #: Form/form_ca.xml.h:400 msgid "1. Vessels and Shanties" -msgstr "" +msgstr "1. Gemiler ve Gecekondular" #: Form/form_ca.xml.h:402 msgid "2. Houses in construction" -msgstr "" +msgstr "2. Yapım aşamasındaki evler" #: Form/form_ca.xml.h:404 msgid "3. Houses unoccupied" -msgstr "" +msgstr "3. Boş evler" #: Form/form_ca.xml.h:406 msgid "4. Houses inhabited" -msgstr "" +msgstr "4. Oturulan evler" #: Form/form_ca.xml.h:408 msgid "5. Families" -msgstr "" +msgstr "5. Aileler" #: Form/form_ca.xml.h:410 msgid "6. Names" -msgstr "" +msgstr "6. Adlar" #: Form/form_ca.xml.h:412 msgid "7. Sex" -msgstr "" +msgstr "7. Cinsiyet" #: Form/form_ca.xml.h:414 msgid "8. Age" -msgstr "" +msgstr "8. Yaş" #: Form/form_ca.xml.h:416 msgid "9. Married or Widowed" -msgstr "" +msgstr "9. Evli veya Dul" #: Form/form_ca.xml.h:417 Form/form_ca.xml.h:470 Form/form_ca.xml.h:628 #: Form/form_ca.xml.h:681 Form/form_ca.xml.h:769 Form/form_ca.xml.h:820 @@ -7781,177 +7768,178 @@ msgstr "" #: Form/form_us.xml.h:1984 Form/form_us.xml.h:2004 RelID/relation_tab.py:340 #: RelID/relation_tab.py:601 msgid "Relation" -msgstr "" +msgstr "İlişki" #: Form/form_ca.xml.h:418 msgid "10. Relationship to Head of Family" -msgstr "" +msgstr "10. Aile Reisiyle İlişki" #: Form/form_ca.xml.h:422 msgid "12. FrenchCanadian" -msgstr "" +msgstr "12. Fransız Kanadalı" #: Form/form_ca.xml.h:423 msgid "BirthPlaceFather" -msgstr "" +msgstr "Babanın Doğum Yeri" #: Form/form_ca.xml.h:424 msgid "13. Birth Place of Father" -msgstr "" +msgstr "13. Babanın Doğum Yeri" #: Form/form_ca.xml.h:425 msgid "BirthPlaceMother" -msgstr "" +msgstr "Annenin Doğum Yeri" #: Form/form_ca.xml.h:426 msgid "14. Birth Place of Mother" -msgstr "" +msgstr "14. Annenin Doğum Yeri" #: Form/form_ca.xml.h:428 msgid "15. Religion" -msgstr "" +msgstr "15. Din" #: Form/form_ca.xml.h:430 msgid "16. Profession, Occupation or Trade" -msgstr "" +msgstr "16. Meslek, İş veya Ticaret" #: Form/form_ca.xml.h:431 Form/form_ca.xml.h:496 Form/form_ca.xml.h:707 #: Form/form_gb.xml.h:164 Form/form_gb.xml.h:293 Form/form_gb.xml.h:525 msgid "Employer" -msgstr "" +msgstr "İşveren" #: Form/form_ca.xml.h:432 msgid "17. Employers" -msgstr "" +msgstr "17. İşverenler" #: Form/form_ca.xml.h:433 Form/form_ca.xml.h:498 Form/form_ca.xml.h:709 msgid "Employee" -msgstr "" +msgstr "Çalışan" #: Form/form_ca.xml.h:434 msgid "18. Wage Earner" -msgstr "" +msgstr "18. Ücretli Çalışan" #: Form/form_ca.xml.h:435 msgid "Unemployed" -msgstr "" +msgstr "İşsiz" #: Form/form_ca.xml.h:436 msgid "19. Unemployed during week preceding Census" -msgstr "" +msgstr "19. Nüfus Sayımından önceki haftada işsiz olanlar" #: Form/form_ca.xml.h:437 msgid "Employees" -msgstr "" +msgstr "Çalışanlar" #: Form/form_ca.xml.h:438 msgid "20. Employer to state average number of hands employed during year" msgstr "" +"20. İşverenin yıl boyunca çalıştırdığı ortalama çalışan sayısını belirtmesi" #: Form/form_ca.xml.h:439 Form/form_ca.xml.h:846 msgid "CanRead" -msgstr "" +msgstr "Okuyabiliyor" #: Form/form_ca.xml.h:440 msgid "21. Instruction: Read" -msgstr "" +msgstr "21. Talimat: Okuyun" #: Form/form_ca.xml.h:441 Form/form_ca.xml.h:848 msgid "CanWrite" -msgstr "" +msgstr "Yazabiliyor" #: Form/form_ca.xml.h:442 msgid "22. Instruction: Write" -msgstr "" +msgstr "22. Talimat: Yazın" #: Form/form_ca.xml.h:444 msgid "23. Deaf and Dumb" -msgstr "" +msgstr "23. Sağır ve Dilsiz" #: Form/form_ca.xml.h:446 msgid "24. Blind" -msgstr "" +msgstr "24. Kör" #: Form/form_ca.xml.h:448 msgid "25. Unsound mind" -msgstr "" +msgstr "25. Akıl sağlığı yerinde değil" #: Form/form_ca.xml.h:449 Form/form_ca.xml.h:528 Form/form_ca.xml.h:660 #: Form/form_ca.xml.h:753 Form/form_ca.xml.h:793 msgid "SubTitle" -msgstr "" +msgstr "Alt Başlık" #: Form/form_ca.xml.h:454 Form/form_ca.xml.h:533 Form/form_ca.xml.h:665 msgid "PollSub" -msgstr "" +msgstr "Anket Alt Başlığı" #: Form/form_ca.xml.h:460 Form/form_ca.xml.h:671 Form/form_ca.xml.h:804 msgid "House" -msgstr "" +msgstr "Ev" #: Form/form_ca.xml.h:461 Form/form_ca.xml.h:672 msgid "1. Dwelling house" -msgstr "" +msgstr "1. Ev" #: Form/form_ca.xml.h:463 Form/form_ca.xml.h:674 msgid "2. Family or household" -msgstr "" +msgstr "2. Aile veya hane halkı" #: Form/form_ca.xml.h:465 Form/form_ca.xml.h:676 msgid "3. Name of each person in family" -msgstr "" +msgstr "3. Ailedeki her bir kişinin adı" #: Form/form_ca.xml.h:467 Form/form_ca.xml.h:631 Form/form_ca.xml.h:768 msgid "4. Sex" -msgstr "" +msgstr "4. Cinsiyet" #: Form/form_ca.xml.h:468 msgid "Colour" -msgstr "" +msgstr "Renk" #: Form/form_ca.xml.h:469 msgid "5. Colour" -msgstr "" +msgstr "5. Renk" #: Form/form_ca.xml.h:471 Form/form_ca.xml.h:682 msgid "6. Relation to head of family" -msgstr "" +msgstr "6. Aile reisiyle ilişkisi" #: Form/form_ca.xml.h:473 Form/form_ca.xml.h:684 msgid "7. Single, married, widowed, divorced or legally separated" -msgstr "" +msgstr "7. Bekâr, evli, dul, boşanmış veya yasal olarak ayrılmış" #: Form/form_ca.xml.h:474 Form/form_ca.xml.h:685 Form/form_ca.xml.h:773 msgid "BirthMonth" -msgstr "" +msgstr "Doğum Ayı" #: Form/form_ca.xml.h:475 msgid "8. Month and Date of Birth" -msgstr "" +msgstr "8. Doğum Ayı ve Tarihi" #: Form/form_ca.xml.h:476 Form/form_ca.xml.h:687 msgid "BirthYear" -msgstr "" +msgstr "Doğum Yılı" #: Form/form_ca.xml.h:477 Form/form_ca.xml.h:688 msgid "9. Year of birth" -msgstr "" +msgstr "9. Doğum yılı" #: Form/form_ca.xml.h:479 Form/form_ca.xml.h:690 msgid "10. Age at last birthday" -msgstr "" +msgstr "10. Son doğum günündeki yaşı" #: Form/form_ca.xml.h:481 Form/form_ca.xml.h:692 msgid "11. Country or place of birth" -msgstr "" +msgstr "11. Doğum ülkesi veya yeri" #: Form/form_ca.xml.h:483 Form/form_ca.xml.h:694 msgid "12. Year of immigration to Canada, if an immigrant" -msgstr "" +msgstr "12. Göçmen ise Kanada'ya göç yılı" #: Form/form_ca.xml.h:485 Form/form_ca.xml.h:696 msgid "13. Year of naturalization, if formerly an alien" -msgstr "" +msgstr "13. Daha önce yabancı uyrukluysa vatandaşlığa kabul yılı" #: Form/form_ca.xml.h:486 Form/form_ca.xml.h:697 Form/form_ca.xml.h:779 #: Form/form_ca.xml.h:838 Form/form_us.xml.h:904 Form/form_us.xml.h:979 @@ -7959,11 +7947,11 @@ msgstr "" #: Form/form_us.xml.h:2364 Form/form_us.xml.h:2437 Form/form_us.xml.h:2467 #: Form/form_us.xml.h:2579 msgid "Race" -msgstr "" +msgstr "Irk" #: Form/form_ca.xml.h:487 Form/form_ca.xml.h:698 msgid "14. Racial or tribal origin" -msgstr "" +msgstr "14. Irksal veya kabilesel köken" #: Form/form_ca.xml.h:488 Form/form_ca.xml.h:699 Form/form_ca.xml.h:836 #: Form/form_fr.xml.h:7 Form/form_fr.xml.h:15 Form/form_fr.xml.h:23 @@ -7975,858 +7963,867 @@ msgstr "" #: Form/form_gb.xml.h:226 Form/form_gb.xml.h:247 Form/form_gb.xml.h:359 #: Form/form_gb.xml.h:382 msgid "Nationality" -msgstr "" +msgstr "Uyruk" #: Form/form_ca.xml.h:489 Form/form_ca.xml.h:700 msgid "15. Nationality" -msgstr "" +msgstr "15. Uyruk" #: Form/form_ca.xml.h:491 Form/form_ca.xml.h:702 msgid "16. Religion" -msgstr "" +msgstr "16. Din" #: Form/form_ca.xml.h:493 msgid "17. Profession, occupation, trade or means of living of each person" -msgstr "" +msgstr "17. Her bir kişinin mesleği, işi, zanaatı veya geçim kaynağı" #: Form/form_ca.xml.h:494 msgid "OwnMeans" -msgstr "" +msgstr "Kendi Geçimi" #: Form/form_ca.xml.h:495 msgid "18. Living on own means" -msgstr "" +msgstr "18. Kendi imkânlarıyla geçiniyor" #: Form/form_ca.xml.h:497 Form/form_ca.xml.h:708 msgid "19. Employer" -msgstr "" +msgstr "19. İşveren" #: Form/form_ca.xml.h:499 Form/form_ca.xml.h:710 msgid "20. Employee" -msgstr "" +msgstr "20. Çalışan" #: Form/form_ca.xml.h:500 Form/form_ca.xml.h:711 msgid "SelfEmployed" -msgstr "" +msgstr "Serbest Meslek" #: Form/form_ca.xml.h:501 Form/form_ca.xml.h:712 msgid "21. Working on own account" -msgstr "" +msgstr "21. Kendi hesabına çalışıyor" #: Form/form_ca.xml.h:502 Form/form_ca.xml.h:713 msgid "WhereEmployed" -msgstr "" +msgstr "Çalıştığı Yer" #: Form/form_ca.xml.h:503 msgid "22. Working at trade in factory or in home" -msgstr "" +msgstr "22. Fabrikada veya evde ticaretle uğraşıyor" #: Form/form_ca.xml.h:504 msgid "MonthsFactory" -msgstr "" +msgstr "Fabrika Ayları" #: Form/form_ca.xml.h:505 msgid "23. Months employed at trade in factory" -msgstr "" +msgstr "23. Fabrikada ticaretle uğraşılan aylar" #: Form/form_ca.xml.h:506 msgid "MonthsHome" -msgstr "" +msgstr "Ev Ayları" #: Form/form_ca.xml.h:507 msgid "24. Months employed at trade in home" -msgstr "" +msgstr "24. Evde ticaretle uğraşılan aylar" #: Form/form_ca.xml.h:508 msgid "MonthsOther" -msgstr "" +msgstr "Diğer Aylar" #: Form/form_ca.xml.h:509 msgid "25. Months employed in other occupation than trace in factory or home" -msgstr "" +msgstr "25. Fabrikada veya evde ticaret dışında başka bir işte çalışılan aylar" #: Form/form_ca.xml.h:510 Form/form_ca.xml.h:723 msgid "EarningsOccupation" -msgstr "" +msgstr "Meslek Kazancı" #: Form/form_ca.xml.h:511 msgid "26. Earnings from occupation or trade" -msgstr "" +msgstr "26. Meslekten veya ticaretten elde edilen kazanç" #: Form/form_ca.xml.h:512 Form/form_ca.xml.h:725 msgid "EarningsOther" -msgstr "" +msgstr "Diğer Kazançlar" #: Form/form_ca.xml.h:513 msgid "27. Extra earning (From other than chief occupation or trade)" -msgstr "" +msgstr "27. Ek kazanç (Ana meslek veya ticaret dışında)" #: Form/form_ca.xml.h:514 Form/form_ca.xml.h:735 Form/form_ca.xml.h:783 msgid "SchoolMonths" -msgstr "" +msgstr "Okul Ayları" #: Form/form_ca.xml.h:515 msgid "28. Months at school in 1910" -msgstr "" +msgstr "28. 1910 yılında okulda geçirilen aylar" #: Form/form_ca.xml.h:516 Form/form_ca.xml.h:737 Form/form_ca.xml.h:785 #: Form/form_us.xml.h:1414 Form/form_us.xml.h:2103 Form/form_us.xml.h:2141 #: Form/form_us.xml.h:2179 Form/form_us.xml.h:2217 msgid "Read" -msgstr "" +msgstr "Oku" #: Form/form_ca.xml.h:517 msgid "29. Can read" -msgstr "" +msgstr "29. Okuyabiliyor" #: Form/form_ca.xml.h:518 Form/form_ca.xml.h:739 Form/form_ca.xml.h:787 #: Form/form_us.xml.h:1415 Form/form_us.xml.h:2104 Form/form_us.xml.h:2142 #: Form/form_us.xml.h:2180 Form/form_us.xml.h:2218 msgid "Write" -msgstr "" +msgstr "Yaz" #: Form/form_ca.xml.h:519 msgid "30. Can write" -msgstr "" +msgstr "30. Yazabiliyor" #: Form/form_ca.xml.h:520 Form/form_ca.xml.h:840 msgid "SpeakEnglish" -msgstr "" +msgstr "İngilizce Konuşabiliyor" #: Form/form_ca.xml.h:521 msgid "31. Can speak English" -msgstr "" +msgstr "31. İngilizce konuşabiliyor" #: Form/form_ca.xml.h:522 Form/form_ca.xml.h:842 msgid "SpeakFrench" -msgstr "" +msgstr "Fransızca Konuşabiliyor" #: Form/form_ca.xml.h:523 msgid "32. Can speak French" -msgstr "" +msgstr "32. Fransızca konuşabiliyor" #: Form/form_ca.xml.h:525 msgid "33. Mother tongue (If spoken)" -msgstr "" +msgstr "33. Ana dili (Konuşuluyorsa)" #: Form/form_ca.xml.h:526 Form/form_ca.xml.h:791 msgid "Infirmities" -msgstr "" +msgstr "Engeller" #: Form/form_ca.xml.h:527 msgid "34. Infirmities: a. Deaf and dumb; b. Blind; c. Unsound mind" -msgstr "" +msgstr "34. Engeller: a. Sağır ve dilsiz; b. Kör; c. Akıl sağlığı yerinde değil" #: Form/form_ca.xml.h:538 msgid "Name from Schedule 1" -msgstr "" +msgstr "Zamanlama 1'den Ad" #: Form/form_ca.xml.h:540 msgid "Line on Schedule 2" -msgstr "" +msgstr "Zamanlama 2'deki Satır" #: Form/form_ca.xml.h:541 msgid "PageRef" -msgstr "" +msgstr "Sayfa Referansı" #: Form/form_ca.xml.h:542 msgid "2. Page from Schedule 1" -msgstr "" +msgstr "2. Zamanlama 1'deki sayfa" #: Form/form_ca.xml.h:543 msgid "LineRef" -msgstr "" +msgstr "Satır Referansı" #: Form/form_ca.xml.h:544 msgid "3. Line from Schedule 1" -msgstr "" +msgstr "3. Zamanlama 1'deki satır" #: Form/form_ca.xml.h:546 msgid "3. Place of Habitation" -msgstr "" +msgstr "3. İkamet Yeri" #: Form/form_ca.xml.h:547 msgid "HousesConst" -msgstr "" +msgstr "Yapım Halindeki Evler" #: Form/form_ca.xml.h:548 msgid "4. Houses: In construction" -msgstr "" +msgstr "4. Evler: Yapım halinde" #: Form/form_ca.xml.h:550 msgid "5. Houses: Vacant" -msgstr "" +msgstr "5. Evler: Boş" #: Form/form_ca.xml.h:552 msgid "6. Houses: Inhabited" -msgstr "" +msgstr "6. Evler: Oturulan" #: Form/form_ca.xml.h:553 Form/form_us.xml.h:789 Form/form_us.xml.h:832 #: Form/form_us.xml.h:891 msgid "Institution" -msgstr "" +msgstr "Kurum" #: Form/form_ca.xml.h:554 msgid "7. Institution: Special or legal name" -msgstr "" +msgstr "7. Kurum: Özel veya yasal adı" #: Form/form_ca.xml.h:555 msgid "Buildings" -msgstr "" +msgstr "Binalar" #: Form/form_ca.xml.h:556 msgid "8. Institution: Number of buildings" -msgstr "" +msgstr "8. Kurum: Bina sayısı" #: Form/form_ca.xml.h:558 msgid "9. Number of families in house or institution" -msgstr "" +msgstr "9. Evde veya kurumda bulunan aile sayısı" #: Form/form_ca.xml.h:559 Form/form_ie.xml.h:7 msgid "Rooms" -msgstr "" +msgstr "Odalar" #: Form/form_ca.xml.h:560 msgid "10. Number of rooms in house or institution for each famiy" -msgstr "" +msgstr "10. Evde veya kurumda her aile için oda sayısı" #: Form/form_ca.xml.h:561 msgid "Inmates" -msgstr "" +msgstr "Oturanlar" #: Form/form_ca.xml.h:562 msgid "11. Number of inmates in institution" -msgstr "" +msgstr "11. Kurumdaki kişi sayısı" #: Form/form_ca.xml.h:563 msgid "OwnedAcres" -msgstr "" +msgstr "Sahip Olunan Dönüm" #: Form/form_ca.xml.h:564 msgid "12. Real Estate Owned: Grand total of acres" -msgstr "" +msgstr "12. Sahip Olunan Gayrimenkul: Toplam dönüm" #: Form/form_ca.xml.h:565 msgid "OwnedLots" -msgstr "" +msgstr "Sahip Olunan Parseller" #: Form/form_ca.xml.h:566 msgid "13. Real Estate Owned: Number of town or village lots" -msgstr "" +msgstr "13. Sahip Olunan Gayrimenkul: Kasaba veya köy parsellerinin sayısı" #: Form/form_ca.xml.h:567 msgid "OwnedHouses" -msgstr "" +msgstr "Sahip Olunan Evler" #: Form/form_ca.xml.h:568 msgid "14. Real Estate Owned: Number of dwelling houses" -msgstr "" +msgstr "14. Sahip Olunan Gayrimenkul: Ev sayısı" #: Form/form_ca.xml.h:569 msgid "OwnedStores" -msgstr "" +msgstr "Sahip Olunan Mağazalar" #: Form/form_ca.xml.h:570 msgid "15. Real Estate Owned: Number of stores, warehouses, etc." -msgstr "" +msgstr "15. Sahip Olunan Gayrimenkul: Mağaza, depo vb. sayısı." #: Form/form_ca.xml.h:571 msgid "OwnedBars" -msgstr "" +msgstr "Sahip Olunan Ahırlar" #: Form/form_ca.xml.h:572 msgid "16. Real Estate Owned: Number of barns, stables and other outbuildings" msgstr "" +"16. Sahip Olunan Gayrimenkul: Ahır, at ahırı ve diğer müştemilatların sayısı" #: Form/form_ca.xml.h:573 msgid "OwnedSilos" -msgstr "" +msgstr "Sahip Olunan Silolar" #: Form/form_ca.xml.h:574 msgid "17. Real Estate Owned: Number of silos and capacity in cubic feet" msgstr "" +"17. Sahip Olunan Gayrimenkul: Silo sayısı ve kübik fit cinsinden kapasiteleri" #: Form/form_ca.xml.h:575 msgid "OwnedFactories" -msgstr "" +msgstr "Sahip Olunan Fabrikalar" #: Form/form_ca.xml.h:576 msgid "18. Real Estate Owned: Number of manufacturing establishments" -msgstr "" +msgstr "18. Sahip Olunan Gayrimenkul: İmalat tesisi sayısı" #: Form/form_ca.xml.h:577 msgid "LeasedAcres" -msgstr "" +msgstr "Kiralanan Dönüm" #: Form/form_ca.xml.h:578 msgid "19. Real Estate Leased: Grand total of acres" -msgstr "" +msgstr "19. Kiralanan Gayrimenkul: Toplam dönüm" #: Form/form_ca.xml.h:579 msgid "LeasedLots" -msgstr "" +msgstr "Kiralanan Parseller" #: Form/form_ca.xml.h:580 msgid "20. Real Estate Leased: Number of town or village lots" -msgstr "" +msgstr "20. Kiralanan Gayrimenkul: Kasaba veya köy parsellerinin sayısı" #: Form/form_ca.xml.h:581 msgid "LeasedHouses" -msgstr "" +msgstr "Kiralanmış Evler" #: Form/form_ca.xml.h:582 msgid "21. Real Estate Leased: Number of dwelling houses" -msgstr "" +msgstr "21. Kiralanan Gayrimenkuller: Ev sayısı" #: Form/form_ca.xml.h:583 msgid "LeasedStores" -msgstr "" +msgstr "Kiralanmış Mağazalar" #: Form/form_ca.xml.h:584 msgid "22. Real Estate Leased: Number of stores, warehouses, etc." -msgstr "" +msgstr "22. Kiralanan Gayrimenkuller: Mağaza, depo vb. sayısı." #: Form/form_ca.xml.h:585 msgid "LeasedBarns" -msgstr "" +msgstr "Kiralanmış Ahırlar" #: Form/form_ca.xml.h:586 msgid "23. Real Estate Leased: Number of barns, stables and other outbuildings" -msgstr "" +msgstr "23. Kiralanan Gayrimenkuller: Ahır, at ahırı ve diğer müştemilat sayısı" #: Form/form_ca.xml.h:587 msgid "LeasedSilos" -msgstr "" +msgstr "Kiralanmış Silolar" #: Form/form_ca.xml.h:588 msgid "24. Real Estate Leased: Number of silos and capacity in cubic feet" msgstr "" +"24. Kiralanan Gayrimenkuller: Silo sayısı ve kübik fit cinsinden kapasiteleri" #: Form/form_ca.xml.h:589 msgid "LeasedFactories" -msgstr "" +msgstr "Kiralanmış Fabrikalar" #: Form/form_ca.xml.h:590 msgid "25. Real Estate Leased: Number of manufacturing establishments" -msgstr "" +msgstr "25. Kiralanan Gayrimenkuller: İmalat tesisi sayısı" #: Form/form_ca.xml.h:591 msgid "Denomination" -msgstr "" +msgstr "Mezhep" #: Form/form_ca.xml.h:592 msgid "26. Church or place of worship: Religious denomination" -msgstr "" +msgstr "26. Kilise veya ibadet yeri: Dini mezhep" #: Form/form_ca.xml.h:593 msgid "Communicants" -msgstr "" +msgstr "Cemaat Üyeleri" #: Form/form_ca.xml.h:594 msgid "27. Church or place of worship: Number of communicants" -msgstr "" +msgstr "27. Kilise veya ibadet yeri: Cemaat üyesi sayısı" #: Form/form_ca.xml.h:595 msgid "Seating" -msgstr "" +msgstr "Oturma" #: Form/form_ca.xml.h:596 msgid "28. Church or place of worship: Seating capacity" -msgstr "" +msgstr "28. Kilise veya ibadet yeri: Oturma kapasitesi" #: Form/form_ca.xml.h:597 msgid "SSDenomination" -msgstr "" +msgstr "Pazar Okulu Mezhebi" #: Form/form_ca.xml.h:598 msgid "29. Sunday School: Religious Denomination" -msgstr "" +msgstr "29. Pazar Okulu: Dini Mezhep" #: Form/form_ca.xml.h:599 msgid "SSTeachers" -msgstr "" +msgstr "Pazar Okulu Öğretmenleri" #: Form/form_ca.xml.h:600 msgid "30. Sunday School: Number of officers and teachers" -msgstr "" +msgstr "30. Pazar Okulu: Görevli ve öğretmen sayısı" #: Form/form_ca.xml.h:601 msgid "SSScholars" -msgstr "" +msgstr "Pazar Okulu Öğrencileri" #: Form/form_ca.xml.h:602 msgid "31. Sunday School: Number of Scholars" -msgstr "" +msgstr "31. Pazar Okulu: Öğrenci sayısı" #: Form/form_ca.xml.h:603 msgid "SchoolRooms" -msgstr "" +msgstr "Okul Odaları" #: Form/form_ca.xml.h:604 msgid "32. School: Number of rooms" -msgstr "" +msgstr "32. Okul: Oda sayısı" #: Form/form_ca.xml.h:605 msgid "SchoolTeachers" -msgstr "" +msgstr "Okul Öğretmenleri" #: Form/form_ca.xml.h:606 msgid "33. School: Number of teachers" -msgstr "" +msgstr "33. Okul: Öğretmen sayısı" #: Form/form_ca.xml.h:607 msgid "SchoolStudents" -msgstr "" +msgstr "Okul Öğrencileri" #: Form/form_ca.xml.h:608 msgid "34. School: Number of Scholars" -msgstr "" +msgstr "34. Okul: Öğrenci sayısı" #: Form/form_ca.xml.h:609 msgid "DateVisit" -msgstr "" +msgstr "Ziyaret Tarihi" #: Form/form_ca.xml.h:610 msgid "35. Date of visit" -msgstr "" +msgstr "35. Ziyaret tarihi" #: Form/form_ca.xml.h:611 msgid "Reason" -msgstr "" +msgstr "Neden" #: Form/form_ca.xml.h:612 msgid "36. The reason, if not enumerated on first visit" -msgstr "" +msgstr "36. İlk ziyarette sayımı yapılamadıysa nedeni" #: Form/form_ca.xml.h:613 msgid "DateEnum" -msgstr "" +msgstr "Sayım Tarihi" #: Form/form_ca.xml.h:614 msgid "37. Date when enumerated" -msgstr "" +msgstr "37. Sayımın yapıldığı tarih" #: Form/form_ca.xml.h:625 msgid "1. No. of family in order of visitation" -msgstr "" +msgstr "1. Ziyaret sırasına göre aile sayısı" #: Form/form_ca.xml.h:627 msgid "2. Name of each person in the family" -msgstr "" +msgstr "2. Ailedeki her bir kişinin adı" #: Form/form_ca.xml.h:629 msgid "3.Relation to head of family" -msgstr "" +msgstr "3. Aile reisiyle ilişkisi" #: Form/form_ca.xml.h:633 msgid "5. Married, single, widowed or divorced" -msgstr "" +msgstr "5. Evli, bekar, dul veya boşanmış" #: Form/form_ca.xml.h:635 msgid "6. Age" -msgstr "" +msgstr "6. Yaş" #: Form/form_ca.xml.h:637 msgid "7. Country or place of birth" -msgstr "" +msgstr "7. Doğduğu ülke veya yer" #: Form/form_ca.xml.h:639 msgid "8. Year of immigration to Canada" -msgstr "" +msgstr "8. Kanada'ya göç yılı" #: Form/form_ca.xml.h:640 msgid "PostOffice" -msgstr "" +msgstr "Posta Ofisi" #: Form/form_ca.xml.h:641 msgid "9. Post office address" -msgstr "" +msgstr "9. Posta ofisi adresi" #: Form/form_ca.xml.h:642 Form/form_us.xml.h:1252 msgid "Section" -msgstr "" +msgstr "Bölüm" #: Form/form_ca.xml.h:643 msgid "10. Location: Section" -msgstr "" +msgstr "10. Konum: Bölüm" #: Form/form_ca.xml.h:645 msgid "11. Location: Township" -msgstr "" +msgstr "11. Konum: Kasaba" #: Form/form_ca.xml.h:647 msgid "12. Location: Range" -msgstr "" +msgstr "12. Konum: Menzil" #: Form/form_ca.xml.h:648 Form/form_ca.xml.h:816 msgid "Meridian" -msgstr "" +msgstr "Meridyen" #: Form/form_ca.xml.h:649 msgid "13. Location: Meridian" -msgstr "" +msgstr "13. Konum: Meridyen" #: Form/form_ca.xml.h:651 msgid "14. Livestock: Horses, all ages" -msgstr "" +msgstr "14. Hayvancılık: Her yaştan at" #: Form/form_ca.xml.h:652 msgid "Cows" -msgstr "" +msgstr "İnekler" #: Form/form_ca.xml.h:653 msgid "15. Livestock: Milch cows" -msgstr "" +msgstr "15. Hayvancılık: Süt inekleri" #: Form/form_ca.xml.h:655 msgid "16. Other horned or meat cattle, all ages" -msgstr "" +msgstr "16. Diğer boynuzlu veya etlik sığırlar, her yaşta" #: Form/form_ca.xml.h:657 msgid "17. Sheep and lambs, all ages" -msgstr "" +msgstr "17. Koyunlar ve kuzular, her yaşta" #: Form/form_ca.xml.h:659 msgid "18. Hogs and pigs, all ages" -msgstr "" +msgstr "18. Domuzlar ve domuz yavruları, her yaşta" #: Form/form_ca.xml.h:678 msgid "4. Place of habitation" -msgstr "" +msgstr "4. İkamet yeri" #: Form/form_ca.xml.h:680 msgid "5. Sex" -msgstr "" +msgstr "5. Cinsiyet" #: Form/form_ca.xml.h:686 msgid "8. Month of birth" -msgstr "" +msgstr "8. Doğum ayı" #: Form/form_ca.xml.h:704 msgid "16. Chief occupation or trade" -msgstr "" +msgstr "16. Başlıca meslek veya iş" #: Form/form_ca.xml.h:705 msgid "OtherEmployment" -msgstr "" +msgstr "Diğer İş" #: Form/form_ca.xml.h:706 msgid "18. Employment other than at chief trade or occupation, if any" -msgstr "" +msgstr "18. Başlıca meslek veya iş dışında yapılan işler, varsa" #: Form/form_ca.xml.h:714 msgid "22. State where person employed" -msgstr "" +msgstr "22. Kişinin çalıştığı eyalet" #: Form/form_ca.xml.h:715 msgid "WeeksOccupation" -msgstr "" +msgstr "Meslek Haftası" #: Form/form_ca.xml.h:716 msgid "23. Weeks employed in 1910 at chief occupation or trade" -msgstr "" +msgstr "23. 1910 yılında başlıca meslek veya işte çalışılan hafta sayısı" #: Form/form_ca.xml.h:717 msgid "WeeksOther" -msgstr "" +msgstr "Diğer Hafta" #: Form/form_ca.xml.h:718 msgid "24. Weeks employed in 1910 at other than chief occupation or trade" -msgstr "" +msgstr "24. 1910 yılında başlıca meslek veya iş dışında çalışılan hafta sayısı" #: Form/form_ca.xml.h:719 msgid "HoursOccupation" -msgstr "" +msgstr "Meslek Saatleri" #: Form/form_ca.xml.h:720 msgid "25. Hours of working time per week at chief occupation" -msgstr "" +msgstr "25. Başlıca meslekte haftalık çalışma saatleri" #: Form/form_ca.xml.h:721 msgid "HoursOther" -msgstr "" +msgstr "Diğer Saatler" #: Form/form_ca.xml.h:722 msgid "26. Hours of working time per week at other occupation, if any" -msgstr "" +msgstr "26. Diğer işte haftalık çalışma saatleri, varsa" #: Form/form_ca.xml.h:724 msgid "27. Total earnings in 1910 from chief occupation or trade" -msgstr "" +msgstr "27. 1910 yılında başlıca meslek veya işten elde edilen toplam kazanç" #: Form/form_ca.xml.h:726 msgid "28. Total earnings in 1910 from other than chief occupation or trade" msgstr "" +"28. 1910 yılında asıl meslek veya zanaat dışındaki işten elde edilen toplam " +"kazanç" #: Form/form_ca.xml.h:727 msgid "Wages" -msgstr "" +msgstr "Ücretler" #: Form/form_ca.xml.h:728 msgid "29. Rate of earnings per hour when employed by the hour-Cents" -msgstr "" +msgstr "29. Saatlik ücretle çalışanlarda saatlik kazanç oranı - Kuruş" #: Form/form_ca.xml.h:729 msgid "InsuranceLife" -msgstr "" +msgstr "Hayat Sigortası" #: Form/form_ca.xml.h:730 msgid "30. Insurance: Upon life" -msgstr "" +msgstr "30. Sigorta: Hayat sigortası" #: Form/form_ca.xml.h:731 msgid "InsuranceAccident" -msgstr "" +msgstr "Kaza Sigortası" #: Form/form_ca.xml.h:732 msgid "31. Insurance: Against accident or illness" -msgstr "" +msgstr "31. Sigorta: Kaza veya hastalık sigortası" #: Form/form_ca.xml.h:733 msgid "InsuranceCost" -msgstr "" +msgstr "Sigorta Maliyeti" #: Form/form_ca.xml.h:734 msgid "32. Insurance: Cost of insurance in census year" -msgstr "" +msgstr "32. Sigorta: Nüfus sayımı yılındaki sigorta maliyeti" #: Form/form_ca.xml.h:736 msgid "33. Months at school in 1910" -msgstr "" +msgstr "33. 1910 yılında okulda geçirilen ay sayısı" #: Form/form_ca.xml.h:738 msgid "34. Can Read" -msgstr "" +msgstr "34. Okuyabiliyor" #: Form/form_ca.xml.h:740 msgid "35. Can Write" -msgstr "" +msgstr "35. Yazabiliyor" #: Form/form_ca.xml.h:742 msgid "36. Language commonly spoken" -msgstr "" +msgstr "36. Yaygın olarak konuşulan dil" #: Form/form_ca.xml.h:743 msgid "SchoolCost" -msgstr "" +msgstr "Okul Maliyeti" #: Form/form_ca.xml.h:744 msgid "" "37. Cost of education in 1910 for persons over 16 years of age at College, " "Convent or University" msgstr "" +"16 yaşından büyük kişiler için 1910 yılında Kolej, Manastır Okulu veya " +"Üniversitedeki eğitim maliyeti" #: Form/form_ca.xml.h:746 msgid "38. Blind" -msgstr "" +msgstr "38. Kör" #: Form/form_ca.xml.h:748 msgid "39. Deaf and dumb" -msgstr "" +msgstr "39. Sağır ve dilsiz" #: Form/form_ca.xml.h:750 msgid "40. Crazy or lunatic" -msgstr "" +msgstr "40. Deli veya akıl hastası" #: Form/form_ca.xml.h:751 Form/form_us.xml.h:481 Form/form_us.xml.h:1083 #: Form/form_us.xml.h:1121 Form/form_us.xml.h:1216 Form/form_us.xml.h:1864 #: Form/form_us.xml.h:2073 msgid "Idiotic" -msgstr "" +msgstr "Geri Zekâlı" #: Form/form_ca.xml.h:752 msgid "41. Idiotic or silly" -msgstr "" +msgstr "41. Geri zekâlı veya ahmak" #: Form/form_ca.xml.h:761 msgid "1. House" -msgstr "" +msgstr "1. Ev" #: Form/form_ca.xml.h:762 msgid "Dwelling house" -msgstr "" +msgstr "Ev" #: Form/form_ca.xml.h:763 msgid "2. Family" -msgstr "" +msgstr "2. Aile" #: Form/form_ca.xml.h:764 msgid "Family or household" -msgstr "" +msgstr "Aile veya hane halkı" #: Form/form_ca.xml.h:765 msgid "3. Name" -msgstr "" +msgstr "3. Ad" #: Form/form_ca.xml.h:766 msgid "Name of each person in family" -msgstr "" +msgstr "Ailedeki her kişinin adı" #: Form/form_ca.xml.h:770 msgid "5. Relation to head of family" -msgstr "" +msgstr "5. Aile reisiyle ilişkisi" #: Form/form_ca.xml.h:772 msgid "6. Single, married, widowed, divorced or legally separated" -msgstr "" +msgstr "6. Bekar, evli, dul, boşanmış veya yasal olarak ayrılmış" #: Form/form_ca.xml.h:774 msgid "7. Month of birth" -msgstr "" +msgstr "7. Doğum ayı" #: Form/form_ca.xml.h:776 msgid "8. Age at last birthday" -msgstr "" +msgstr "8. Son doğum günündeki yaşı" #: Form/form_ca.xml.h:778 msgid "9. Country or place of birth" -msgstr "" +msgstr "9. Doğduğu ülke veya yer" #: Form/form_ca.xml.h:780 msgid "10. Racial or tribal origin" -msgstr "" +msgstr "10. Irksal veya kabilesel köken" #: Form/form_ca.xml.h:782 msgid "11. Religion" -msgstr "" +msgstr "11. Din" #: Form/form_ca.xml.h:784 msgid "12. Months at school in 1910" -msgstr "" +msgstr "12. 1910 yılında okulda geçirilen ay sayısı" #: Form/form_ca.xml.h:786 msgid "13. Can Read" -msgstr "" +msgstr "13. Okuma becerisi" #: Form/form_ca.xml.h:788 msgid "14. Can Write" -msgstr "" +msgstr "14. Yazma becerisi" #: Form/form_ca.xml.h:790 msgid "15. Language commonly spoken" -msgstr "" +msgstr "15. Yaygın olarak konuşulan dil" #: Form/form_ca.xml.h:792 msgid "" "16. Infirmities: a. blind; b. deaf and dumb; c. idotic or silly; d. crazy or " "lunatic" msgstr "" +"16. Engeller: a. kör; b. sağır ve dilsiz; c. aptal veya zihinsel engelli; d. " +"deli veya akıl hastası" #: Form/form_ca.xml.h:805 msgid "1. Dwelling House" -msgstr "" +msgstr "1. İkametgah" #: Form/form_ca.xml.h:807 msgid "2. Family, Household or Institution" -msgstr "" +msgstr "2. Aile, Hane Halkı veya Kurum" #: Form/form_ca.xml.h:809 msgid "3. Name of each person in family, household or institution" -msgstr "" +msgstr "3. Aile, hane halkı veya kurumdaki her kişinin adı" #: Form/form_ca.xml.h:810 msgid "MilitaryService" -msgstr "" +msgstr "Askerlik Hizmeti" #: Form/form_ca.xml.h:811 msgid "4. Military Service" -msgstr "" +msgstr "4. Askerlik Hizmeti" #: Form/form_ca.xml.h:813 msgid "5. Place of Habitation: Township" -msgstr "" +msgstr "5. İkamet Yeri: İlçe" #: Form/form_ca.xml.h:815 msgid "6. Place of Habitation: Range" -msgstr "" +msgstr "6. İkamet Yeri: Bölge" #: Form/form_ca.xml.h:817 msgid "7. Place of Habitation: Meridian" -msgstr "" +msgstr "7. İkamet Yeri: Meridyen" #: Form/form_ca.xml.h:819 msgid "8. Place of Habitation: Municipality" -msgstr "" +msgstr "8. İkamet Yeri: Belediye" #: Form/form_ca.xml.h:821 msgid "9. Relationship to head of family or household" -msgstr "" +msgstr "9. Aile veya hane halkı reisiyle ilişkisi" #: Form/form_ca.xml.h:823 msgid "10. Sex" -msgstr "" +msgstr "10. Cinsiyet" #: Form/form_ca.xml.h:825 msgid "11. Single, married, widowed, divorced or legally separated" -msgstr "" +msgstr "11. Bekâr, evli, dul, boşanmış veya yasal olarak ayrı" #: Form/form_ca.xml.h:827 msgid "12. Age at last birthday" -msgstr "" +msgstr "12. Son doğum günündeki yaş" #: Form/form_ca.xml.h:829 msgid "13. Country or place of birth" -msgstr "" +msgstr "13. Doğduğu ülke veya yer" #: Form/form_ca.xml.h:831 msgid "14. Religion" -msgstr "" +msgstr "14. Din" #: Form/form_ca.xml.h:833 msgid "15. Year of immigration to Canada" -msgstr "" +msgstr "15. Kanada'ya göç yılı" #: Form/form_ca.xml.h:835 msgid "16. Year of naturalization" -msgstr "" +msgstr "16. Vatandaşlığa kabul yılı" #: Form/form_ca.xml.h:837 msgid "17. Nationality" -msgstr "" +msgstr "17. Uyruk" #: Form/form_ca.xml.h:839 msgid "18. Race or tribal origin" -msgstr "" +msgstr "18. Irk veya kabilesel köken" #: Form/form_ca.xml.h:841 msgid "19. Can speak English" -msgstr "" +msgstr "19. İngilizce konuşma becerisi" #: Form/form_ca.xml.h:843 msgid "20. Can speak French" -msgstr "" +msgstr "20. Fransızca konuşma becerisi" #: Form/form_ca.xml.h:844 msgid "OtherLanguage" -msgstr "" +msgstr "Diğer Dil" #: Form/form_ca.xml.h:845 msgid "21. Other language spoken as mother tongue" -msgstr "" +msgstr "21. Anadil olarak konuşulan diğer dil" #: Form/form_ca.xml.h:847 msgid "22. Can Read" -msgstr "" +msgstr "22. Okuma becerisi" #: Form/form_ca.xml.h:849 msgid "23. Can Write" -msgstr "" +msgstr "23. Yazma becerisi" #: Form/form_ca.xml.h:851 msgid "24. Chief occupation or trade" -msgstr "" +msgstr "24. Başlıca meslek veya ticaret" #: Form/form_ca.xml.h:853 msgid "25. Employer, \"E\", Employee, \"W\"" -msgstr "" +msgstr "25. İşveren, \"D\", Çalışan, \"B\"" #: Form/form_ca.xml.h:854 msgid "EmploymentPlace" -msgstr "" +msgstr "İş Yeri" #: Form/form_ca.xml.h:855 msgid "26. State where person is employed" -msgstr "" +msgstr "26. Kişinin çalıştığı eyalet" #: Form/form_dk.xml.h:2 Form/form_dk.xml.h:19 Form/form_dk.xml.h:36 #: Form/form_dk.xml.h:51 Form/form_dk.xml.h:68 Form/form_dk.xml.h:85 @@ -8836,7 +8833,7 @@ msgstr "" #: Form/form_dk.xml.h:313 Form/form_dk.xml.h:337 Form/form_dk.xml.h:363 #: Form/form_dk.xml.h:387 Form/form_dk.xml.h:407 Form/form_dk.xml.h:427 msgid "Amt" -msgstr "" +msgstr "İlçe" #: Form/form_dk.xml.h:3 Form/form_dk.xml.h:20 Form/form_dk.xml.h:37 #: Form/form_dk.xml.h:52 Form/form_dk.xml.h:69 Form/form_dk.xml.h:86 @@ -8846,7 +8843,7 @@ msgstr "" #: Form/form_dk.xml.h:314 Form/form_dk.xml.h:338 Form/form_dk.xml.h:364 #: Form/form_dk.xml.h:388 Form/form_dk.xml.h:408 Form/form_dk.xml.h:428 msgid "Town or City" -msgstr "" +msgstr "Kasaba veya Şehir" #: Form/form_dk.xml.h:4 Form/form_dk.xml.h:21 Form/form_dk.xml.h:38 #: Form/form_dk.xml.h:53 Form/form_dk.xml.h:70 Form/form_dk.xml.h:87 @@ -8856,7 +8853,7 @@ msgstr "" #: Form/form_dk.xml.h:315 Form/form_dk.xml.h:339 Form/form_dk.xml.h:365 #: Form/form_dk.xml.h:389 Form/form_dk.xml.h:409 Form/form_dk.xml.h:429 msgid "Herred eller By" -msgstr "" +msgstr "İlçe veya Şehir" #: Form/form_dk.xml.h:6 Form/form_dk.xml.h:23 Form/form_dk.xml.h:40 #: Form/form_dk.xml.h:55 Form/form_dk.xml.h:72 Form/form_dk.xml.h:89 @@ -8866,7 +8863,7 @@ msgstr "" #: Form/form_dk.xml.h:317 Form/form_dk.xml.h:341 Form/form_dk.xml.h:367 #: Form/form_dk.xml.h:391 Form/form_dk.xml.h:411 Form/form_dk.xml.h:431 msgid "Sogn" -msgstr "" +msgstr "Mahalle" #: Form/form_dk.xml.h:8 Form/form_dk.xml.h:25 Form/form_dk.xml.h:42 #: Form/form_dk.xml.h:57 Form/form_dk.xml.h:74 Form/form_dk.xml.h:91 @@ -8876,7 +8873,7 @@ msgstr "" #: Form/form_dk.xml.h:319 Form/form_dk.xml.h:343 Form/form_dk.xml.h:369 #: Form/form_dk.xml.h:393 Form/form_dk.xml.h:413 Form/form_dk.xml.h:433 msgid "Gade" -msgstr "" +msgstr "Sokak" #: Form/form_dk.xml.h:10 Form/form_dk.xml.h:27 Form/form_dk.xml.h:44 #: Form/form_dk.xml.h:59 Form/form_dk.xml.h:76 Form/form_dk.xml.h:93 @@ -8897,7 +8894,7 @@ msgstr "" #: Form/form_us.xml.h:1645 Form/form_us.xml.h:2406 Overview/Overview.py:79 #: Overview/Overview.py:114 msgid "Condition" -msgstr "" +msgstr "Medeni durum" #: Form/form_dk.xml.h:11 Form/form_dk.xml.h:28 Form/form_dk.xml.h:45 #: Form/form_dk.xml.h:60 Form/form_dk.xml.h:77 Form/form_dk.xml.h:94 @@ -8905,7 +8902,7 @@ msgstr "" #: Form/form_dk.xml.h:170 Form/form_dk.xml.h:193 Form/form_dk.xml.h:214 #: Form/form_dk.xml.h:235 Form/form_dk.xml.h:259 Form/form_dk.xml.h:294 msgid "Gift" -msgstr "" +msgstr "Evli" #: Form/form_dk.xml.h:13 Form/form_dk.xml.h:30 Form/form_dk.xml.h:47 #: Form/form_dk.xml.h:62 Form/form_dk.xml.h:79 Form/form_dk.xml.h:96 @@ -8913,7 +8910,7 @@ msgstr "" #: Form/form_dk.xml.h:172 Form/form_dk.xml.h:195 Form/form_dk.xml.h:216 #: Form/form_dk.xml.h:237 msgid "Alder" -msgstr "" +msgstr "Yaş" #: Form/form_dk.xml.h:15 Form/form_dk.xml.h:32 Form/form_dk.xml.h:49 #: Form/form_dk.xml.h:66 Form/form_dk.xml.h:83 Form/form_dk.xml.h:100 @@ -8923,15 +8920,15 @@ msgstr "" #: Form/form_dk.xml.h:332 Form/form_dk.xml.h:348 Form/form_dk.xml.h:380 #: Form/form_dk.xml.h:400 Form/form_dk.xml.h:422 Form/form_dk.xml.h:442 msgid "Stilling i familien" -msgstr "" +msgstr "Aile içindeki konumu" #: Form/form_dk.xml.h:17 msgid "Titel, Embed, Forretning, Håndværk" -msgstr "" +msgstr "Unvan, Görev, Ticaret, Zanaat" #: Form/form_dk.xml.h:34 msgid "Stilling, embede eller erhverv" -msgstr "" +msgstr "Görev, makam veya meslek" #: Form/form_dk.xml.h:63 Form/form_dk.xml.h:80 Form/form_dk.xml.h:97 #: Form/form_dk.xml.h:116 Form/form_dk.xml.h:135 Form/form_dk.xml.h:152 @@ -8951,7 +8948,7 @@ msgstr "" #: Form/form_gb.xml.h:529 Form/form_gb.xml.h:566 Form/form_gb.xml.h:589 #: Form/form_gb.xml.h:621 Overview/Overview.py:78 Overview/Overview.py:112 msgid "Where Born" -msgstr "" +msgstr "Doğum yeri" #: Form/form_dk.xml.h:64 Form/form_dk.xml.h:81 Form/form_dk.xml.h:98 #: Form/form_dk.xml.h:117 Form/form_dk.xml.h:136 Form/form_dk.xml.h:153 @@ -8960,21 +8957,21 @@ msgstr "" #: Form/form_dk.xml.h:324 Form/form_dk.xml.h:374 Form/form_dk.xml.h:398 #: Form/form_dk.xml.h:420 Form/form_dk.xml.h:438 msgid "Fødested" -msgstr "" +msgstr "Doğum Yeri" #: Form/form_dk.xml.h:115 Form/form_dk.xml.h:134 msgid "Erhverv" -msgstr "" +msgstr "İş Yeri" #: Form/form_dk.xml.h:155 Form/form_dk.xml.h:176 Form/form_dk.xml.h:199 #: Form/form_dk.xml.h:220 Form/form_dk.xml.h:241 Form/form_dk.xml.h:269 #: Form/form_dk.xml.h:304 Form/form_dk.xml.h:326 msgid "Trossamfund" -msgstr "" +msgstr "Dini Topluluk" #: Form/form_dk.xml.h:167 Form/form_dk.xml.h:190 msgid "Sted" -msgstr "" +msgstr "Konum" #: Form/form_dk.xml.h:179 Form/form_dk.xml.h:202 Form/form_dk.xml.h:223 #: Form/form_dk.xml.h:247 Form/form_dk.xml.h:281 Form/form_dk.xml.h:310 @@ -8990,25 +8987,25 @@ msgstr "" #: Form/form_fr.xml.h:180 Form/form_fr.xml.h:189 Form/form_fr.xml.h:198 #: Form/form_fr.xml.h:207 Form/form_fr.xml.h:223 msgid "Comments" -msgstr "" +msgstr "Yorumlar" #: Form/form_dk.xml.h:180 Form/form_dk.xml.h:203 Form/form_dk.xml.h:224 #: Form/form_dk.xml.h:248 Form/form_dk.xml.h:282 Form/form_dk.xml.h:311 msgid "Bemærkninger" -msgstr "" +msgstr "Çalışma Yeri" #: Form/form_dk.xml.h:245 Form/form_dk.xml.h:273 Form/form_dk.xml.h:308 #: Form/form_dk.xml.h:334 Form/form_dk.xml.h:350 Form/form_dk.xml.h:382 #: Form/form_dk.xml.h:424 Form/form_dk.xml.h:444 Form/form_gb.xml.h:253 #: Form/form_gb.xml.h:388 msgid "Place of Work" -msgstr "" +msgstr "İş Yeri" #: Form/form_dk.xml.h:246 Form/form_dk.xml.h:274 Form/form_dk.xml.h:309 #: Form/form_dk.xml.h:335 Form/form_dk.xml.h:351 Form/form_dk.xml.h:383 #: Form/form_dk.xml.h:425 Form/form_dk.xml.h:445 msgid "Arbejdssted" -msgstr "" +msgstr "Çalışma yeri" #: Form/form_dk.xml.h:260 Form/form_dk.xml.h:295 Form/form_dk.xml.h:321 #: Form/form_dk.xml.h:345 Form/form_dk.xml.h:371 Form/form_dk.xml.h:395 @@ -9016,163 +9013,163 @@ msgstr "" #: Form/form_ie.xml.h:51 Form/form_us.xml.h:985 Form/form_us.xml.h:1009 #: Form/form_us.xml.h:2374 Form/form_us.xml.h:2563 msgid "Date of Birth" -msgstr "" +msgstr "Doğum tarihi" #: Form/form_dk.xml.h:261 Form/form_dk.xml.h:296 Form/form_dk.xml.h:322 #: Form/form_dk.xml.h:346 Form/form_dk.xml.h:372 Form/form_dk.xml.h:396 #: Form/form_dk.xml.h:418 Form/form_dk.xml.h:436 msgid "Fødselsdato" -msgstr "" +msgstr "Doğum tarihi" #: Form/form_dk.xml.h:264 Form/form_dk.xml.h:299 Form/form_dk.xml.h:375 msgid "Years at current residence" -msgstr "" +msgstr "Mevcut ikamette geçirilen yıllar" #: Form/form_dk.xml.h:265 Form/form_dk.xml.h:300 Form/form_dk.xml.h:376 msgid "År tilflyttet" -msgstr "" +msgstr "Taşınılan yıl" #: Form/form_dk.xml.h:266 Form/form_dk.xml.h:301 Form/form_dk.xml.h:377 msgid "Previous residence" -msgstr "" +msgstr "Önceki ikamet yeri" #: Form/form_dk.xml.h:267 Form/form_dk.xml.h:302 Form/form_dk.xml.h:378 msgid "Tilflyttet fra" -msgstr "" +msgstr "Taşındığı yer" #: Form/form_dk.xml.h:275 Form/form_us.xml.h:2095 Form/form_us.xml.h:2133 #: Form/form_us.xml.h:2171 Form/form_us.xml.h:2209 msgid "Year married" -msgstr "" +msgstr "Evlilik yılı" #: Form/form_dk.xml.h:276 msgid "År gift" -msgstr "" +msgstr "Evlenilen yıl" #: Form/form_dk.xml.h:277 Form/form_gb.xml.h:212 Form/form_gb.xml.h:345 #: Form/form_gb.xml.h:620 msgid "Children Living" -msgstr "" +msgstr "Yaşayan çocuklar" #: Form/form_dk.xml.h:278 msgid "Levende børn" -msgstr "" +msgstr "Yaşayan çocuklar" #: Form/form_dk.xml.h:279 msgid "Children Dead" -msgstr "" +msgstr "Ölen çocuklar" #: Form/form_dk.xml.h:280 msgid "Døde børn" -msgstr "" +msgstr "Ölen çocuklar" #: Form/form_dk.xml.h:292 msgid "Køn" -msgstr "" +msgstr "Cinsiyet" #: Form/form_dk.xml.h:327 msgid "Year stayed in parish" -msgstr "" +msgstr "Cemaatte ikamet etmeye başlanan yıl" #: Form/form_dk.xml.h:328 msgid "Hvilket aar taget ophold i sognet" -msgstr "" +msgstr "Hangi yıl cemaatte ikamet etmeye başlandı" #: Form/form_dk.xml.h:329 msgid "Previous Residence" -msgstr "" +msgstr "Önceki ikamet yeri" #: Form/form_dk.xml.h:330 msgid "Sidste bopæl inden tilflytning" -msgstr "" +msgstr "Taşınmadan önceki son ikamet yeri" #: Form/form_dk.xml.h:353 Form/form_dk.xml.h:385 Form/form_dk.xml.h:405 msgid "Bemærkning" -msgstr "" +msgstr "Not" #: Form/form_dk.xml.h:354 msgid "Income" -msgstr "" +msgstr "Gelir" #: Form/form_dk.xml.h:355 msgid "Indkomst" -msgstr "" +msgstr "Gelirler" #: Form/form_dk.xml.h:356 msgid "Assets" -msgstr "" +msgstr "Mal varlığı" #: Form/form_dk.xml.h:357 msgid "Formue" -msgstr "" +msgstr "Servet" #: Form/form_dk.xml.h:358 msgid "State Tax" -msgstr "" +msgstr "Devlet vergisi" #: Form/form_dk.xml.h:359 msgid "Statsskat" -msgstr "" +msgstr "Devlet vergisi" #: Form/form_dk.xml.h:360 msgid "Council" -msgstr "" +msgstr "Belediye" #: Form/form_dk.xml.h:361 msgid "Kommuneskat" -msgstr "" +msgstr "Belediye vergisi" #: Form/form_dk.xml.h:402 msgid "Residence on 5 Nov 1924" -msgstr "" +msgstr "5 Kasım 1924 tarihindeki ikamet yeri" #: Form/form_dk.xml.h:403 msgid "Bopæl 5 nov 1924" -msgstr "" +msgstr "5 Kasım 1924 tarihindeki ikamet yeri" #: Form/form_dk.xml.h:415 msgid "Stay on 5 Nov 1929" -msgstr "" +msgstr "5 Kasım 1929 tarihindeki ikamet" #: Form/form_dk.xml.h:416 msgid "Ophold 5 nov 1929" -msgstr "" +msgstr "5 Kasım 1929 tarihindeki ikamet" #: Form/form_dk.xml.h:439 msgid "Date Married" -msgstr "" +msgstr "Evlilik tarihi" #: Form/form_dk.xml.h:440 msgid "Dato gift" -msgstr "" +msgstr "Evlenme tarihi" #: Form/form_fr.xml.h:2 Form/form_fr.xml.h:10 Form/form_fr.xml.h:18 #: Form/form_fr.xml.h:26 Form/form_fr.xml.h:138 Form/form_fr.xml.h:210 #: Form/form_us.xml.h:2352 Form/form_us.xml.h:2527 msgid "Date of birth" -msgstr "" +msgstr "Doğum tarihi" #: Form/form_fr.xml.h:38 Form/form_fr.xml.h:55 Form/form_fr.xml.h:220 msgid "Can read and write" -msgstr "" +msgstr "Okuyup yazabiliyor" #: Form/form_fr.xml.h:42 Form/form_fr.xml.h:47 msgid "Military" -msgstr "" +msgstr "Askerlik" #: Form/form_fr.xml.h:51 Form/form_fr.xml.h:156 Form/form_fr.xml.h:173 #: Form/form_fr.xml.h:182 Form/form_fr.xml.h:191 Form/form_fr.xml.h:200 #: Form/form_us.xml.h:677 msgid "Year of birth" -msgstr "" +msgstr "Doğum yılı" #: Form/form_fr.xml.h:56 Form/form_fr.xml.h:221 msgid "Taxes" -msgstr "" +msgstr "Vergiler" #: Form/form_fr.xml.h:57 Form/form_fr.xml.h:67 Form/form_fr.xml.h:222 msgid "Type of roof" -msgstr "" +msgstr "Çatı tipi" #: Form/form_fr.xml.h:81 Form/form_fr.xml.h:219 Form/form_gb.xml.h:83 #: Form/form_gb.xml.h:101 Form/form_gb.xml.h:122 Form/form_gb.xml.h:143 @@ -9180,68 +9177,68 @@ msgstr "" #: Form/form_gb.xml.h:298 Form/form_gb.xml.h:325 Form/form_gb.xml.h:361 #: Form/form_gb.xml.h:592 Form/form_gb.xml.h:624 Form/form_us.xml.h:2546 msgid "Disability" -msgstr "" +msgstr "Engellilik durumu" #: Form/form_fr.xml.h:144 msgid "Arrived since" -msgstr "" +msgstr "Geliş tarihi" #: Form/form_gb.xml.h:1 Form/form_gb.xml.h:19 Form/form_gb.xml.h:38 #: Form/form_us.xml.h:2343 Form/form_us.xml.h:2401 Form/form_us.xml.h:2427 #: Form/form_us.xml.h:2459 msgid "Certificate Number" -msgstr "" +msgstr "Sertifika Numarası" #: Form/form_gb.xml.h:2 Form/form_gb.xml.h:20 Form/form_gb.xml.h:39 #: Form/form_us.xml.h:2344 Form/form_us.xml.h:2402 Form/form_us.xml.h:2428 #: Form/form_us.xml.h:2460 msgid "Entry Number" -msgstr "" +msgstr "Giriş Numarası" #: Form/form_gb.xml.h:3 Form/form_gb.xml.h:40 Form/form_gb.xml.h:196 #: Form/form_gb.xml.h:230 Form/form_gb.xml.h:329 Form/form_gb.xml.h:365 msgid "Registration District" -msgstr "" +msgstr "Kayıt Bölgesi" #: Form/form_gb.xml.h:4 Form/form_gb.xml.h:21 Form/form_gb.xml.h:41 msgid "Administrative Area" -msgstr "" +msgstr "İdari Bölge" #: Form/form_gb.xml.h:5 Form/form_gb.xml.h:22 Form/form_gb.xml.h:42 msgid "County/Administrative Area" -msgstr "" +msgstr "İl/İdari Bölge" #: Form/form_gb.xml.h:6 Form/form_gb.xml.h:43 Form/form_us.xml.h:2345 #: Form/form_us.xml.h:2429 Form/form_us.xml.h:2461 msgid "Date Registered" -msgstr "" +msgstr "Kayıt Tarihi" #: Form/form_gb.xml.h:7 Form/form_gb.xml.h:44 Form/form_us.xml.h:2346 #: Form/form_us.xml.h:2430 Form/form_us.xml.h:2462 msgid "Registrar" -msgstr "" +msgstr "Kayıt Memuru" #: Form/form_gb.xml.h:13 Form/form_gb.xml.h:47 Form/form_ie.xml.h:63 msgid "Maiden Surname" -msgstr "" +msgstr "Kızlık Soyadı" #: Form/form_gb.xml.h:16 Form/form_gb.xml.h:25 Form/form_gb.xml.h:37 #: Form/form_gb.xml.h:56 Form/form_ie.xml.h:99 Form/form_ie.xml.h:101 #: Form/form_ie.xml.h:131 Form/form_us.xml.h:1055 msgid "Signed" -msgstr "" +msgstr "İmzalandı" #: Form/form_gb.xml.h:23 msgid "Banns/Licence" -msgstr "" +msgstr "Nikah İlanı/Evlilik Belgesi" #: Form/form_gb.xml.h:31 Form/form_gb.xml.h:34 msgid "Deceased" -msgstr "" +msgstr "Vefat Eden" #: Form/form_gb.xml.h:48 msgid "Maiden surname of woman who was married" -msgstr "" +msgstr "Evli kadının kızlık soyadı" #: Form/form_gb.xml.h:51 Form/form_ie.xml.h:49 Form/form_us.xml.h:986 #: Form/form_us.xml.h:1008 Form/form_us.xml.h:1141 Form/form_us.xml.h:1155 @@ -9252,123 +9249,126 @@ msgstr "Doğum Yeri" #: Form/form_gb.xml.h:53 msgid "Usual Address" -msgstr "" +msgstr "Olağan Adres" #: Form/form_gb.xml.h:59 Form/form_gb.xml.h:71 msgid "City or Borough" -msgstr "" +msgstr "Şehir veya İlçe" #: Form/form_gb.xml.h:60 Form/form_gb.xml.h:69 Form/form_gb.xml.h:85 msgid "Parish or Township" -msgstr "" +msgstr "Mahalle veya Belde" #: Form/form_gb.xml.h:62 msgid "NAMES of each Person who abode therein the preceding Night." -msgstr "" +msgstr "Bir Önceki Gece Orada İkamet Eden Her Kişinin ADLARI." #: Form/form_gb.xml.h:64 msgid "" "AGE, rounded down to the nearest five years for those aged fifteen or over." msgstr "" +"YAŞ, on beş yaş ve üzerindekiler için en yakın alt beş yıla yuvarlanmış." #: Form/form_gb.xml.h:66 msgid "PROFESSION, TRADE, EMPLOYMENT or of INDEPENDENT MEANS." -msgstr "" +msgstr "MESLEK, TİCARET, İŞ veya BAĞIMSIZ GELİR." #: Form/form_gb.xml.h:68 msgid "" "Whether Born in same County (Y or N), Scotland (S), Ireland (I) or Foreign " "Parts (P)." msgstr "" +"Aynı İlçede (E veya H), İskoçya'da (S), İrlanda'da (I) veya Yabancı " +"Ülkelerde (P) doğmuş olup olmadığı." #: Form/form_gb.xml.h:70 Form/form_gb.xml.h:91 Form/form_gb.xml.h:110 msgid "Ecclesiastical District" -msgstr "" +msgstr "Kilise Bölgesi" #: Form/form_gb.xml.h:75 msgid "" "Name and Surname of each Person who abode in the house, on the Night of the " "30th March, 1851" -msgstr "" +msgstr "30 Mart 1851 Gecesi Evde Bulunan Her Kişinin Adı ve Soyadı" #: Form/form_gb.xml.h:77 Form/form_gb.xml.h:95 Form/form_gb.xml.h:433 #: Form/form_gb.xml.h:460 Form/form_gb.xml.h:488 Form/form_gb.xml.h:520 #: Form/form_gb.xml.h:559 Form/form_gb.xml.h:581 Form/form_gb.xml.h:610 msgid "Relation to Head of Family" -msgstr "" +msgstr "Aile Reisiyle İlişkisi" #: Form/form_gb.xml.h:81 msgid "Rank, Profession or Occupation" -msgstr "" +msgstr "Rütbe, Meslek veya İş" #: Form/form_gb.xml.h:84 msgid "Whether Blind or Deaf and Dumb" -msgstr "" +msgstr "Kör veya Sağır ve Dilsiz Olup Olmadığı" #: Form/form_gb.xml.h:86 Form/form_gb.xml.h:104 Form/form_gb.xml.h:125 msgid "City or Municipal Borough" -msgstr "" +msgstr "Şehir veya Belediye Bölgesi" #: Form/form_gb.xml.h:87 Form/form_gb.xml.h:105 Form/form_gb.xml.h:126 #: Form/form_gb.xml.h:148 Form/form_gb.xml.h:277 msgid "Municipal Ward" -msgstr "" +msgstr "Belediye Mahallesi" #: Form/form_gb.xml.h:88 Form/form_gb.xml.h:106 Form/form_gb.xml.h:127 #: Form/form_gb.xml.h:601 msgid "Parliamentary Borough" -msgstr "" +msgstr "Parlamento Bölgesi" #: Form/form_gb.xml.h:90 msgid "Hamlet or Tything" -msgstr "" +msgstr "Köy veya İlçe" #: Form/form_gb.xml.h:93 Form/form_gb.xml.h:180 Form/form_gb.xml.h:311 msgid "Name and Surname of each Person" -msgstr "" +msgstr "Her Kişinin Adı ve Soyadı" #: Form/form_gb.xml.h:99 msgid "Rank, Profession of Occupation" -msgstr "" +msgstr "Rütbe, Meslek veya İş" #: Form/form_gb.xml.h:102 msgid "Whether Blind, or Deaf-and-Dumb" -msgstr "" +msgstr "Kör veya Sağır ve Dilsiz Olup Olmadığı" #: Form/form_gb.xml.h:103 Form/form_gb.xml.h:124 msgid "Civil Parish or Township" -msgstr "" +msgstr "Sivil Bölge veya Kasaba" #: Form/form_gb.xml.h:108 Form/form_gb.xml.h:451 Form/form_gb.xml.h:479 #: Form/form_gb.xml.h:510 Form/form_gb.xml.h:549 msgid "Village or Hamlet" -msgstr "" +msgstr "Köy veya Mezra" #: Form/form_gb.xml.h:109 msgid "Local Board or Improvement Commissioners District" -msgstr "" +msgstr "Yerel Yönetim Kurulu veya İyileştirme Komiserleri Bölgesi" #: Form/form_gb.xml.h:112 Form/form_gb.xml.h:133 Form/form_gb.xml.h:155 #: Form/form_gb.xml.h:284 msgid "NAME and Surname of each Person" -msgstr "" +msgstr "Her Kişinin ADI ve Soyadı" #: Form/form_gb.xml.h:114 Form/form_gb.xml.h:135 Form/form_gb.xml.h:157 #: Form/form_gb.xml.h:182 Form/form_gb.xml.h:286 Form/form_gb.xml.h:313 msgid "RELATION to Head of Family" -msgstr "" +msgstr "Aile Reisiyle İLİŞKİSİ" #: Form/form_gb.xml.h:116 msgid "CONDITION" -msgstr "" +msgstr "MEDENİ DURUM" #: Form/form_gb.xml.h:118 msgid "AGE of" -msgstr "" +msgstr "YAŞI" #: Form/form_gb.xml.h:120 Form/form_gb.xml.h:141 msgid "Rank, Profession or OCCUPATION" -msgstr "" +msgstr "Rütbe, Meslek veya İş" #: Form/form_gb.xml.h:123 msgid "" @@ -9378,30 +9378,35 @@ msgid "" "3. Imbecile or Idiot\n" "4. Lunatic" msgstr "" +"Şunlardan biri olup olmadığı\n" +"1. Sağır ve Dilsiz\n" +"2. Kör\n" +"3. Zihinsel Engelli veya Ahmak\n" +"4. Akıl Hastası" #: Form/form_gb.xml.h:128 Form/form_gb.xml.h:150 Form/form_gb.xml.h:279 msgid "Town, Village or Hamlet" -msgstr "" +msgstr "Kasaba, Köy veya Mezra" #: Form/form_gb.xml.h:129 Form/form_gb.xml.h:149 Form/form_gb.xml.h:278 msgid "Urban Sanitary District" -msgstr "" +msgstr "Kentsel Sağlık Bölgesi" #: Form/form_gb.xml.h:130 Form/form_gb.xml.h:151 Form/form_gb.xml.h:280 msgid "Rural Sanitary District" -msgstr "" +msgstr "Kırsal Sağlık Bölgesi" #: Form/form_gb.xml.h:131 Form/form_gb.xml.h:153 Form/form_gb.xml.h:282 msgid "Ecclesiastical Parish or District" -msgstr "" +msgstr "Kilise Bölgesi veya İlçesi" #: Form/form_gb.xml.h:137 Form/form_gb.xml.h:159 Form/form_gb.xml.h:288 msgid "CONDITION as to Marriage" -msgstr "" +msgstr "Evlilik DURUMU" #: Form/form_gb.xml.h:139 Form/form_gb.xml.h:161 Form/form_gb.xml.h:290 msgid "AGE last Birthday of" -msgstr "" +msgstr "Son Doğum Günündeki YAŞ" #: Form/form_gb.xml.h:144 msgid "" @@ -9411,43 +9416,48 @@ msgid "" "(3) Imbecile or Idiot\n" "(4) Lunatic" msgstr "" +"Şunlardan biri ise\n" +"(1) Sağır ve Dilsiz\n" +"(2) Kör\n" +"(3) Zihinsel Engelli veya Ahmak\n" +"(4) Akıl Hastası" #: Form/form_gb.xml.h:145 Form/form_gb.xml.h:171 Form/form_gb.xml.h:274 #: Form/form_gb.xml.h:302 msgid "Administrative County" -msgstr "" +msgstr "İdari İlçe" #: Form/form_gb.xml.h:146 Form/form_gb.xml.h:172 Form/form_gb.xml.h:275 #: Form/form_gb.xml.h:303 Form/form_gb.xml.h:445 Form/form_gb.xml.h:472 #: Form/form_gb.xml.h:500 Form/form_gb.xml.h:537 msgid "Civil Parish" -msgstr "" +msgstr "Sivil Bölge" #: Form/form_gb.xml.h:147 Form/form_gb.xml.h:276 msgid "Municipal Borough" -msgstr "" +msgstr "Belediye İlçesi" #: Form/form_gb.xml.h:152 Form/form_gb.xml.h:177 Form/form_gb.xml.h:281 #: Form/form_gb.xml.h:308 msgid "Parliamentary Borough or Division" -msgstr "" +msgstr "Parlamento İlçesi veya Seçim Bölgesi" #: Form/form_gb.xml.h:163 Form/form_gb.xml.h:292 msgid "PROFESSION or OCCUPATION" -msgstr "" +msgstr "MESLEK veya İŞ" #: Form/form_gb.xml.h:165 Form/form_gb.xml.h:294 Form/form_gb.xml.h:526 #: Form/form_us.xml.h:819 msgid "Employed" -msgstr "" +msgstr "Çalışan" #: Form/form_gb.xml.h:166 Form/form_gb.xml.h:295 Form/form_gb.xml.h:527 msgid "Neither" -msgstr "" +msgstr "Hiçbiri" #: Form/form_gb.xml.h:167 Form/form_gb.xml.h:296 Form/form_gb.xml.h:528 msgid "Neither Employer nor Employed" -msgstr "" +msgstr "Ne İşveren Ne de Çalışan" #: Form/form_gb.xml.h:170 Form/form_gb.xml.h:299 msgid "" @@ -9456,56 +9466,60 @@ msgid "" "(2) Blind\n" "(3) Lunatic, Imbecile or Idiot" msgstr "" +"Eğer şunlardan biriyse\n" +"(1) Sağır ve Kör\n" +"(2) Kör\n" +"(3) Akıl Hastası, Zihinsel Engelli veya Ahmak" #: Form/form_gb.xml.h:173 Form/form_gb.xml.h:304 Form/form_gb.xml.h:539 msgid "Ecclesiastical Parish" -msgstr "" +msgstr "Kilise Bölgesi" #: Form/form_gb.xml.h:174 Form/form_gb.xml.h:305 msgid "County Borough, Municipal Borough, or Urban District" -msgstr "" +msgstr "Kontluk İlçe Belediyesi, Belediye İlçesi veya Kentsel Bölge" #: Form/form_gb.xml.h:175 Form/form_gb.xml.h:306 msgid "Ward of Municipal Borough or of Urban District" -msgstr "" +msgstr "Belediye İlçesi veya Kentsel Bölgenin Mahallesi" #: Form/form_gb.xml.h:176 Form/form_gb.xml.h:307 msgid "Rural District" -msgstr "" +msgstr "Kırsal Bölge" #: Form/form_gb.xml.h:178 Form/form_gb.xml.h:309 msgid "Town or Village or Hamlet" -msgstr "" +msgstr "Kasaba, Köy veya Mezra" #: Form/form_gb.xml.h:184 Form/form_gb.xml.h:315 Form/form_gb.xml.h:561 msgid "Condition as to Marriage" -msgstr "" +msgstr "Evlilik Durumu" #: Form/form_gb.xml.h:186 Form/form_gb.xml.h:317 msgid "Age last Birthday of" -msgstr "" +msgstr "Son Doğum Günündeki Yaş" #: Form/form_gb.xml.h:188 Form/form_gb.xml.h:319 msgid "PROFESSION OR OCCUPATION" -msgstr "" +msgstr "MESLEK VEYA İŞ" #: Form/form_gb.xml.h:189 Form/form_gb.xml.h:220 Form/form_gb.xml.h:320 #: Form/form_gb.xml.h:353 msgid "Work Type" -msgstr "" +msgstr "Çalışma Türü" #: Form/form_gb.xml.h:190 Form/form_gb.xml.h:321 msgid "Employer, Worker or Own Account" -msgstr "" +msgstr "İşveren, Çalışan veya Kendi Hesabına Çalışan" #: Form/form_gb.xml.h:191 Form/form_gb.xml.h:222 Form/form_gb.xml.h:322 #: Form/form_gb.xml.h:355 msgid "At Home" -msgstr "" +msgstr "Evde" #: Form/form_gb.xml.h:192 Form/form_gb.xml.h:323 msgid "If Working at Home" -msgstr "" +msgstr "Evden Çalışıyorsa" #: Form/form_gb.xml.h:195 Form/form_gb.xml.h:326 msgid "" @@ -9515,129 +9529,136 @@ msgid "" "(3) Lunatic\n" "(4) Imbecile, feeble-minded" msgstr "" +"Eğer şunlardan biriyse\n" +"(1) Sağır ve Dilsiz\n" +"(2) Kör\n" +"(3) Akıl Hastası\n" +"(4) Zeka Geriliği Olan, Zihinsel Engelli" #: Form/form_gb.xml.h:197 Form/form_gb.xml.h:231 Form/form_gb.xml.h:330 #: Form/form_gb.xml.h:366 msgid "Registration Sub-District" -msgstr "" +msgstr "Kayıt Alt Bölgesi" #: Form/form_gb.xml.h:198 Form/form_gb.xml.h:232 Form/form_gb.xml.h:331 #: Form/form_gb.xml.h:367 Form/form_gb.xml.h:424 msgid "Enumeration District" -msgstr "" +msgstr "Sayım Bölgesi" #: Form/form_gb.xml.h:199 Form/form_gb.xml.h:233 Form/form_gb.xml.h:332 #: Form/form_gb.xml.h:368 msgid "Number of Rooms" -msgstr "" +msgstr "Oda Sayısı" #: Form/form_gb.xml.h:201 Form/form_gb.xml.h:334 msgid "NAME AND SURNAME" -msgstr "" +msgstr "ADI VE SOYADI" #: Form/form_gb.xml.h:203 Form/form_gb.xml.h:336 msgid "RELATIONSHIP to Head of Family" -msgstr "" +msgstr "Aile Reisi ile İLİŞKİSİ" #: Form/form_gb.xml.h:205 Form/form_gb.xml.h:338 msgid "AGE (last Birthday)" -msgstr "" +msgstr "YAŞ (son doğum günündeki)" #: Form/form_gb.xml.h:207 Form/form_gb.xml.h:340 msgid "" "\"Single,\" \"Married,\" \"Widower,\" or \"Widow,\" for all persons aged 15 " "years and upwards." msgstr "" +"15 yaş ve üzeri tüm kişiler için \"Bekar\", \"Evli\", \"Dul Erkek\" veya " +"\"Dul Kadın\" durumu." #: Form/form_gb.xml.h:208 Form/form_gb.xml.h:341 Form/form_gb.xml.h:617 #: Form/form_ie.xml.h:32 msgid "Years Married" -msgstr "" +msgstr "Evlilik Yılı Süresi" #: Form/form_gb.xml.h:209 Form/form_gb.xml.h:342 msgid "Completed Years the present Marriage has lasted." -msgstr "" +msgstr "Mevcut evliliğin sürdüğü tamamlanmış yıl olarak süresi." #: Form/form_gb.xml.h:210 Form/form_gb.xml.h:343 msgid "Children Total" -msgstr "" +msgstr "Toplam Çocuk" #: Form/form_gb.xml.h:211 Form/form_gb.xml.h:344 msgid "Total Children Born Alive." -msgstr "" +msgstr "Doğumundan beri hayatta olan toplam çocuk sayısı." #: Form/form_gb.xml.h:213 Form/form_gb.xml.h:346 msgid "Children still Living." -msgstr "" +msgstr "Hala hayatta olan çocuklar." #: Form/form_gb.xml.h:214 Form/form_gb.xml.h:347 msgid "Children Died" -msgstr "" +msgstr "Ölen Çocuklar" #: Form/form_gb.xml.h:215 Form/form_gb.xml.h:348 msgid "Children who have Died." -msgstr "" +msgstr "Ölmüş Çocuklar." #: Form/form_gb.xml.h:217 Form/form_gb.xml.h:350 Form/form_ie.xml.h:30 msgid "Personal Occupation" -msgstr "" +msgstr "Kişisel Meslek" #: Form/form_gb.xml.h:218 Form/form_gb.xml.h:351 Form/form_us.xml.h:778 #: Form/form_us.xml.h:817 Form/form_us.xml.h:864 Form/form_us.xml.h:1617 msgid "Industry" -msgstr "" +msgstr "Sektör" #: Form/form_gb.xml.h:219 Form/form_gb.xml.h:352 msgid "Industry or Service with which worker is connected." -msgstr "" +msgstr "Çalışanın bağlı olduğu sektör veya hizmet." #: Form/form_gb.xml.h:221 Form/form_gb.xml.h:354 msgid "Whether Employer, Worker or Working on Own Account" -msgstr "" +msgstr "İşveren, Çalışan veya Kendi Hesabına Çalışan Olup Olmadığı" #: Form/form_gb.xml.h:223 Form/form_gb.xml.h:356 msgid "Whether Working at Home" -msgstr "" +msgstr "Evden Çalışıp Çalışmadığı" #: Form/form_gb.xml.h:225 Form/form_gb.xml.h:358 msgid "BIRTHPLACE of every Person." -msgstr "" +msgstr "Her Kişinin DOĞUM YERİ." #: Form/form_gb.xml.h:227 Form/form_gb.xml.h:360 msgid "NATIONALITY of every Person born in a Foreign Country." -msgstr "" +msgstr "Yabancı Bir Ülkede Doğan Her Kişinin UYRUĞU." #: Form/form_gb.xml.h:229 Form/form_gb.xml.h:362 msgid "INFIRMITY" -msgstr "" +msgstr "ENGELLİLİK" #: Form/form_gb.xml.h:235 Form/form_gb.xml.h:370 msgid "NAME and SURNAME" -msgstr "" +msgstr "ADI ve SOYADI" #: Form/form_gb.xml.h:237 Form/form_gb.xml.h:372 msgid "RELATIONSHIP to Head of Household" -msgstr "" +msgstr "Aile Reisiyle İLİŞKİSİ" #: Form/form_gb.xml.h:239 Form/form_gb.xml.h:374 msgid "AGE years" -msgstr "" +msgstr "Yıl olarak YAŞ" #: Form/form_gb.xml.h:240 Form/form_gb.xml.h:375 msgid "Age Months" -msgstr "" +msgstr "Ay Olarak Yaş" #: Form/form_gb.xml.h:241 Form/form_gb.xml.h:376 msgid "AGE months" -msgstr "" +msgstr "Ay olarak YAŞ" #: Form/form_gb.xml.h:243 Form/form_gb.xml.h:378 msgid "SEX" -msgstr "" +msgstr "CİNSİYET" #: Form/form_gb.xml.h:245 Form/form_gb.xml.h:380 msgid "MARRIAGE or ORPHANHOOD" -msgstr "" +msgstr "EVLİLİK veya YETİMLİK" #: Form/form_gb.xml.h:246 Form/form_gb.xml.h:381 Form/form_ie.xml.h:26 #: Form/form_us.xml.h:1390 Form/form_us.xml.h:1642 Form/form_us.xml.h:1686 @@ -9647,290 +9668,299 @@ msgstr "" #: Form/form_us.xml.h:2415 Form/form_us.xml.h:2419 Form/form_us.xml.h:2423 #: Form/form_us.xml.h:2448 Form/form_us.xml.h:2470 msgid "Birthplace" -msgstr "" +msgstr "Doğum Yeri" #: Form/form_gb.xml.h:249 Form/form_gb.xml.h:384 msgid "" "If attending a School or any kind of Educational Institution for the purpose " "of receiving Instruction" msgstr "" +"Öğretim almak amacıyla bir okula veya herhangi bir eğitim kurumuna devam " +"edip etmediği" #: Form/form_gb.xml.h:251 Form/form_gb.xml.h:386 msgid "" "State here the precise branch of Profession, Trade, Manufacture, Service, &c." msgstr "" +"Buraya meslek, ticaret, imalat, hizmet vb. alanın tam ve açık adını yazınız." #: Form/form_gb.xml.h:254 Form/form_gb.xml.h:389 msgid "Number of living children" -msgstr "" +msgstr "Yaşayan çocuk sayısı" #: Form/form_gb.xml.h:255 Form/form_gb.xml.h:390 msgid "Total number under sixteen years of age. If none write \"None.\"" -msgstr "" +msgstr "On altı yaşın altındaki toplam çocuk sayısı. Eğer yoksa \"Yok\" yazın" #: Form/form_gb.xml.h:256 Form/form_gb.xml.h:391 msgid "Ages of living children" -msgstr "" +msgstr "Yaşayan çocukların yaşları" #: Form/form_gb.xml.h:257 Form/form_gb.xml.h:392 msgid "For each child place a X in the column corresponding to its age." -msgstr "" +msgstr "Her çocuk için, yaşına karşılık gelen sütuna bir X işareti koyun." #: Form/form_gb.xml.h:258 msgid "E.D. Letter Code" -msgstr "" +msgstr "E.D. Harf Kodu" #: Form/form_gb.xml.h:259 msgid "Borough, U.D. or R.D." -msgstr "" +msgstr "İlçe, U.D. veya R.D." #: Form/form_gb.xml.h:260 msgid "Registration District and Sub-district" -msgstr "" +msgstr "Kayıt Bölgesi ve Alt Bölgesi" #: Form/form_gb.xml.h:262 msgid "SURNAMES and OTHER NAMES." -msgstr "" +msgstr "SOYADLAR ve DİĞER ADLAR." #: Form/form_gb.xml.h:263 msgid "OVSPI" -msgstr "" +msgstr "OVSPI" #: Form/form_gb.xml.h:264 msgid "O, V, S, P or I" -msgstr "" +msgstr "O, V, S, P veya I" #: Form/form_gb.xml.h:266 msgid "M or F" -msgstr "" +msgstr "E veya K" #: Form/form_gb.xml.h:269 msgid "S, M, W or D." -msgstr "" +msgstr "B, E, D veya B." #: Form/form_gb.xml.h:271 msgid "PERSONAL OCCUPATION" -msgstr "" +msgstr "KİŞİSEL MESLEK" #: Form/form_gb.xml.h:272 msgid "Instructions" -msgstr "" +msgstr "Talimatlar" #: Form/form_gb.xml.h:273 msgid "See INSTRUCTIONS" -msgstr "" +msgstr "TALİMATLARA bakın" #: Form/form_gb.xml.h:301 Form/form_gb.xml.h:328 Form/form_gb.xml.h:364 msgid "Language Spoken" -msgstr "" +msgstr "Konuşulan Dil" #: Form/form_gb.xml.h:394 msgid "LANGUAGE SPOKEN." -msgstr "" +msgstr "KONUŞULAN DİL." #: Form/form_gb.xml.h:397 Form/form_gb.xml.h:408 Form/form_gb.xml.h:425 #: Form/form_gb.xml.h:452 Form/form_gb.xml.h:480 Form/form_gb.xml.h:512 #: Form/form_gb.xml.h:551 msgid "GROS Data" -msgstr "" +msgstr "GROS Verileri" #: Form/form_gb.xml.h:401 msgid "Born in County" -msgstr "" +msgstr "Doğduğu İlçe" #: Form/form_gb.xml.h:402 msgid "Foreigner, Born England or Ireland" -msgstr "" +msgstr "Yabancı, İngiltere veya İrlanda Doğumlu" #: Form/form_gb.xml.h:404 Form/form_gb.xml.h:419 Form/form_gb.xml.h:446 #: Form/form_gb.xml.h:473 Form/form_gb.xml.h:501 Form/form_gb.xml.h:540 msgid "Quoad Sacra Parish" -msgstr "" +msgstr "Quoad Sacra Bölgesi" #: Form/form_gb.xml.h:405 Form/form_gb.xml.h:420 Form/form_gb.xml.h:447 #: Form/form_gb.xml.h:475 Form/form_gb.xml.h:503 Form/form_gb.xml.h:542 msgid "Parliamentary Burgh" -msgstr "" +msgstr "Parlamento Kasabası" #: Form/form_gb.xml.h:406 Form/form_gb.xml.h:421 Form/form_gb.xml.h:448 #: Form/form_gb.xml.h:476 Form/form_gb.xml.h:505 Form/form_gb.xml.h:544 msgid "Royal Burgh" -msgstr "" +msgstr "Kraliyet Kasabası" #: Form/form_gb.xml.h:407 Form/form_gb.xml.h:604 msgid "Town or Village" -msgstr "" +msgstr "Kasaba veya Köy" #: Form/form_gb.xml.h:410 msgid "House Schedule No" -msgstr "" +msgstr "Ev Sıra Numarası" #: Form/form_gb.xml.h:412 msgid "Relation to Head" -msgstr "" +msgstr "Aile Reisiyle İlişkisi" #: Form/form_gb.xml.h:417 msgid "Blind, or Deaf and Dumb" -msgstr "" +msgstr "Kör veya Sağır ve Dilsiz" #: Form/form_gb.xml.h:427 Form/form_gb.xml.h:454 Form/form_gb.xml.h:482 #: Form/form_gb.xml.h:514 Form/form_gb.xml.h:553 msgid "No. Of Schedule" -msgstr "" +msgstr "Zamanlama Numarası" #: Form/form_gb.xml.h:429 Form/form_gb.xml.h:456 Form/form_gb.xml.h:484 #: Form/form_gb.xml.h:516 Form/form_gb.xml.h:555 msgid "Road, Street, etc, and No. or Name of House" -msgstr "" +msgstr "Yol, Sokak vb. ve Ev Numarası veya Adı" #: Form/form_gb.xml.h:430 Form/form_gb.xml.h:457 Form/form_gb.xml.h:485 #: Form/form_gb.xml.h:517 Form/form_gb.xml.h:556 msgid "House Inhabited" -msgstr "" +msgstr "İkamet Edilen Ev" #: Form/form_gb.xml.h:431 Form/form_gb.xml.h:458 Form/form_gb.xml.h:486 #: Form/form_gb.xml.h:518 Form/form_gb.xml.h:557 msgid "House U or B" -msgstr "" +msgstr "Ev U veya B" #: Form/form_gb.xml.h:437 Form/form_gb.xml.h:464 Form/form_gb.xml.h:492 #: Form/form_gb.xml.h:524 msgid "Rank, Profession, or Occupation" -msgstr "" +msgstr "Rütbe, Meslek veya İş" #: Form/form_gb.xml.h:439 Form/form_gb.xml.h:466 Form/form_gb.xml.h:494 #: Form/form_gb.xml.h:531 Form/form_gb.xml.h:568 Form/form_us.xml.h:1497 #: RelID/relation_tab.py:448 msgid "Disabled" -msgstr "" +msgstr "Engelli" #: Form/form_gb.xml.h:440 msgid "Whether Blind, or Deaf and Dumb" -msgstr "" +msgstr "Kör veya Sağır ve Dilsiz Olup Olmadığı" #: Form/form_gb.xml.h:442 Form/form_gb.xml.h:469 Form/form_gb.xml.h:497 #: Form/form_gb.xml.h:534 Form/form_gb.xml.h:571 msgid "No. of Children from 5 to 13 attending School" -msgstr "" +msgstr "Okula Giden 5-13 Yaş Arası Çocuk Sayısı" #: Form/form_gb.xml.h:443 Form/form_gb.xml.h:470 Form/form_gb.xml.h:498 #: Form/form_gb.xml.h:535 Form/form_gb.xml.h:572 msgid "WindowRooms" -msgstr "" +msgstr "Pencereli Odalar" #: Form/form_gb.xml.h:444 Form/form_gb.xml.h:471 Form/form_gb.xml.h:499 #: Form/form_gb.xml.h:536 Form/form_gb.xml.h:573 msgid "No. of Rooms with one or more Windows" -msgstr "" +msgstr "Bir veya Daha Fazla Penceresi Olan Oda Sayısı" #: Form/form_gb.xml.h:449 Form/form_gb.xml.h:477 Form/form_gb.xml.h:507 #: Form/form_gb.xml.h:546 msgid "Police Burgh" -msgstr "" +msgstr "Polis Kasabası" #: Form/form_gb.xml.h:467 Form/form_gb.xml.h:495 Form/form_gb.xml.h:532 #: Form/form_gb.xml.h:569 msgid "Whether 1. Deaf and Dumb, 2. Blind, 3. Imbecile or Idiot 4. Lunatic" msgstr "" +"1. Sağır ve Dilsiz, 2. Kör, 3. Zihinsel Engelli veya Ahmak, 4. Akıl Hastası " +"olup olmadığı" #: Form/form_gb.xml.h:474 msgid "School Board District" -msgstr "" +msgstr "Okul Kurulu Bölgesi" #: Form/form_gb.xml.h:502 Form/form_gb.xml.h:541 msgid "School board District" -msgstr "" +msgstr "Okul Kurulu Bölgesi" #: Form/form_gb.xml.h:504 Form/form_gb.xml.h:543 Form/form_gb.xml.h:602 msgid "Parliamentary Division" -msgstr "" +msgstr "Parlamento Bölgesi" #: Form/form_gb.xml.h:506 Form/form_gb.xml.h:545 msgid "Municipal Burgh" -msgstr "" +msgstr "Belediye Kasabası" #: Form/form_gb.xml.h:508 Form/form_gb.xml.h:547 msgid "Burgh Ward" -msgstr "" +msgstr "Kasaba Mahallesi" #: Form/form_gb.xml.h:511 Form/form_gb.xml.h:550 msgid "Island" -msgstr "" +msgstr "İzlanda" #: Form/form_gb.xml.h:530 Form/form_gb.xml.h:567 msgid "Gaellic or G. & E." -msgstr "" +msgstr "Galce veya G. ve E." #: Form/form_gb.xml.h:538 msgid "Parish Ward" -msgstr "" +msgstr "Bölge Mahallesi" #: Form/form_gb.xml.h:563 msgid "Profession or Occupation" -msgstr "" +msgstr "Meslek veya İş" #: Form/form_gb.xml.h:564 msgid "Employer, Worker or on Own Account" -msgstr "" +msgstr "İşveren, İşçi veya Kendi Hesabına Çalışan" #: Form/form_gb.xml.h:565 msgid "Working at Home" -msgstr "" +msgstr "Evden Çalışıyor" #: Form/form_gb.xml.h:575 Form/form_gb.xml.h:596 msgid "District Electoral Division" -msgstr "" +msgstr "Bölge Seçim Birimi" #: Form/form_gb.xml.h:576 Form/form_gb.xml.h:599 Form/form_ie.xml.h:10 msgid "Townland" -msgstr "" +msgstr "Kasaba" #: Form/form_gb.xml.h:578 Form/form_gb.xml.h:607 msgid "No. on Form B" -msgstr "" +msgstr "B Formundaki Numara" #: Form/form_gb.xml.h:584 Form/form_gb.xml.h:613 msgid "\"Read and Write\", \"Read\" or \"Cannot Read\"" -msgstr "" +msgstr "\"Okur ve Yazar\", \"Okur\" veya \"Okuyamaz\"" #: Form/form_gb.xml.h:590 Form/form_gb.xml.h:622 Form/form_ie.xml.h:27 msgid "Irish Language" -msgstr "" +msgstr "İrlanda Dili" #: Form/form_gb.xml.h:591 Form/form_gb.xml.h:623 msgid "" "\"Irish\" or \"Irish & English\", in other cases no entry should be made" msgstr "" +"\"İrlandaca\" veya \"İrlandaca ve İngilizce\" dışında herhangi bir giriş " +"yapılmamalıdır" #: Form/form_gb.xml.h:593 Form/form_gb.xml.h:625 msgid "" "\"Deaf and Dumb\", \"Dumb only\", \"Blind\", \"Imbecile or Idiot\", or " "\"Lunatic\"" msgstr "" +"\"Sağır ve Dilsiz\", \"Yalnızca Dilsiz\", \"Kör\", \"Zekâ Geriliği Olan veya " +"Ahmak\", ya da \"Akıl Hastası\"" #: Form/form_gb.xml.h:595 msgid "Poor Law Union" -msgstr "" +msgstr "Yoksullar Yasası Birliği" #: Form/form_gb.xml.h:597 msgid "Barony" -msgstr "" +msgstr "Baronluk" #: Form/form_gb.xml.h:603 msgid "Urban District" -msgstr "" +msgstr "Kentsel Bölge" #: Form/form_gb.xml.h:606 msgid "Head of Family" -msgstr "" +msgstr "Aile Reisi" #: Form/form_gb.xml.h:618 msgid "Total Children" -msgstr "" +msgstr "Toplam Çocuk" #: Form/form_gb.xml.h:619 msgid "Children born alive to present Marriage" -msgstr "" +msgstr "Mevcut evlilikten canlı doğan çocuklar" #: Form/form_ie.xml.h:1 msgid "Total Area in Statute Acres" @@ -10030,7 +10060,7 @@ msgstr "16 Yaş Altında Yaşayan Toplam Çocuk Sayısı" #: Form/form_ie.xml.h:39 msgid "(1.) Entry no." -msgstr "(1.) Kayıt numarası" +msgstr "(1.) Giriş numarası" #: Form/form_ie.xml.h:40 Form/form_ie.xml.h:74 Form/form_ie.xml.h:102 msgid "Superintendent Registrar's District" @@ -10198,52 +10228,52 @@ msgstr "(9.) Örneğin: Ölüm anında hazır bulunan" #: Form/form_pl.xml.h:5 msgid "Event Place (Original)" -msgstr "" +msgstr "Etkinlik Yeri (Orijinal)" #: Form/form_pl.xml.h:8 Form/form_pl.xml.h:36 msgid "Birth Year (Estimated)" -msgstr "" +msgstr "Doğum Yılı (Tahmini)" #: Form/form_pl.xml.h:9 Form/form_pl.xml.h:26 Form/form_pl.xml.h:37 #: Form/form_us.xml.h:1453 Form/form_us.xml.h:2449 Form/form_us.xml.h:2472 #: Form/form_us.xml.h:2498 msgid "Father's Name" -msgstr "" +msgstr "Babanın Adı" #: Form/form_pl.xml.h:10 Form/form_pl.xml.h:27 Form/form_pl.xml.h:38 #: Form/form_us.xml.h:1456 Form/form_us.xml.h:2451 Form/form_us.xml.h:2474 #: Form/form_us.xml.h:2499 msgid "Mother's Name" -msgstr "" +msgstr "Annenin Adı" #: Form/form_pl.xml.h:11 Form/form_pl.xml.h:39 Form/form_us.xml.h:2439 #: Form/form_us.xml.h:2479 msgid "Spouse's Name" -msgstr "" +msgstr "Eşinin Adı" #: Form/form_pl.xml.h:12 Form/form_pl.xml.h:40 msgid "Spouse's Gender" -msgstr "" +msgstr "Eşinin Cinsiyeti" #: Form/form_pl.xml.h:13 Form/form_pl.xml.h:41 msgid "Spouse's Age" -msgstr "" +msgstr "Eşinin Yaşı" #: Form/form_pl.xml.h:14 Form/form_pl.xml.h:42 msgid "Spouse's Birth Year (Estimated)" -msgstr "" +msgstr "Eşinin Doğum Yılı (Tahmini)" #: Form/form_pl.xml.h:15 Form/form_pl.xml.h:43 msgid "Spouse's Father's Name" -msgstr "" +msgstr "Eşinin Babasının Adı" #: Form/form_pl.xml.h:16 Form/form_pl.xml.h:44 msgid "Spouse's Mother's Name" -msgstr "" +msgstr "Eşinin Annesinin Adı" #: Form/form_pl.xml.h:18 msgid "Record Number" -msgstr "" +msgstr "Kayıt Numarası" #: Form/form_pl.xml.h:19 msgid "Volume" @@ -10251,7 +10281,7 @@ msgstr "Cilt" #: Form/form_pl.xml.h:24 Form/form_us.xml.h:1426 msgid "House Number" -msgstr "" +msgstr "Ev Numarası" #: Form/form_pl.xml.h:28 msgid "Volume Beginning Year" @@ -10270,7 +10300,7 @@ msgstr "Cilt Bitiş Yılı" #: Form/form_us.xml.h:743 Form/form_us.xml.h:781 Form/form_us.xml.h:824 #: Form/form_us.xml.h:885 Form/form_us.xml.h:1092 msgid "NARA publication" -msgstr "" +msgstr "NARA yayını" #: Form/form_us.xml.h:2 Form/form_us.xml.h:15 Form/form_us.xml.h:36 #: Form/form_us.xml.h:57 Form/form_us.xml.h:97 Form/form_us.xml.h:166 @@ -10281,7 +10311,7 @@ msgstr "" #: Form/form_us.xml.h:744 Form/form_us.xml.h:782 Form/form_us.xml.h:825 #: Form/form_us.xml.h:886 Form/form_us.xml.h:1093 msgid "Roll No." -msgstr "" +msgstr "Kayıt No." #: Form/form_us.xml.h:3 Form/form_us.xml.h:16 Form/form_us.xml.h:37 #: Form/form_us.xml.h:58 Form/form_us.xml.h:98 Form/form_us.xml.h:167 @@ -10299,7 +10329,7 @@ msgstr "" #: Form/form_us.xml.h:2268 Form/form_us.xml.h:2286 Form/form_us.xml.h:2296 #: Form/form_us.xml.h:2315 Form/form_us.xml.h:2325 Form/form_us.xml.h:2502 msgid "Page No." -msgstr "" +msgstr "Sayfa No." #: Form/form_us.xml.h:4 Form/form_us.xml.h:17 Form/form_us.xml.h:38 #: Form/form_us.xml.h:59 Form/form_us.xml.h:99 Form/form_us.xml.h:168 @@ -10309,7 +10339,7 @@ msgstr "" #: Form/form_us.xml.h:836 Form/form_us.xml.h:894 Form/form_us.xml.h:1095 #: Form/form_us.xml.h:1678 msgid "Sheet No." -msgstr "" +msgstr "Sayfa No." #: Form/form_us.xml.h:5 Form/form_us.xml.h:18 Form/form_us.xml.h:39 #: Form/form_us.xml.h:60 Form/form_us.xml.h:100 Form/form_us.xml.h:169 @@ -10317,7 +10347,7 @@ msgstr "" #: Form/form_us.xml.h:460 Form/form_us.xml.h:496 Form/form_us.xml.h:608 #: Form/form_us.xml.h:645 Form/form_us.xml.h:1098 Form/form_us.xml.h:1844 msgid "Minor Civil Division" -msgstr "" +msgstr "Küçük Sivil Bölüm" #: Form/form_us.xml.h:6 Form/form_us.xml.h:19 Form/form_us.xml.h:40 #: Form/form_us.xml.h:61 Form/form_us.xml.h:101 Form/form_us.xml.h:170 @@ -10332,32 +10362,32 @@ msgstr "" #: Form/form_us.xml.h:1877 Form/form_us.xml.h:1898 Form/form_us.xml.h:1927 #: Form/form_us.xml.h:1981 Form/form_us.xml.h:2289 Form/form_us.xml.h:2318 msgid "County of" -msgstr "" +msgstr "İlçesi" #: Form/form_us.xml.h:7 Form/form_us.xml.h:20 Form/form_us.xml.h:41 #: Form/form_us.xml.h:62 Form/form_us.xml.h:102 Form/form_us.xml.h:171 msgid "District (or Territory) of" -msgstr "" +msgstr "Bölgesi (veya Toprakları)" #: Form/form_us.xml.h:9 msgid "Free white males 16 & up" -msgstr "" +msgstr "16 yaş ve üzeri özgür beyaz erkekler" #: Form/form_us.xml.h:10 msgid "Free white males under 16" -msgstr "" +msgstr "16 yaş altı özgür beyaz erkekler" #: Form/form_us.xml.h:11 msgid "Free white females" -msgstr "" +msgstr "Özgür beyaz kadınlar" #: Form/form_us.xml.h:12 Form/form_us.xml.h:33 Form/form_us.xml.h:54 msgid "All other free persons" -msgstr "" +msgstr "Diğer tüm özgür kişiler" #: Form/form_us.xml.h:13 Form/form_us.xml.h:34 Form/form_us.xml.h:55 msgid "Slaves" -msgstr "" +msgstr "Köleler" #: Form/form_us.xml.h:21 Form/form_us.xml.h:42 Form/form_us.xml.h:63 #: Form/form_us.xml.h:103 Form/form_us.xml.h:172 Form/form_us.xml.h:258 @@ -10369,455 +10399,457 @@ msgstr "" #: Form/form_us.xml.h:1636 Form/form_us.xml.h:1651 Form/form_us.xml.h:1846 #: Form/form_us.xml.h:1899 Form/form_us.xml.h:1928 Form/form_us.xml.h:2329 msgid "Enumeration Date" -msgstr "" +msgstr "Sayım Tarihi" #: Form/form_us.xml.h:23 Form/form_us.xml.h:44 Form/form_us.xml.h:65 msgid "Free white males under 10" -msgstr "" +msgstr "10 yaş altı özgür beyaz erkekler" #: Form/form_us.xml.h:24 Form/form_us.xml.h:45 Form/form_us.xml.h:66 msgid "Free white males 10-15" -msgstr "" +msgstr "10-15 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:25 Form/form_us.xml.h:46 Form/form_us.xml.h:68 msgid "Free white males 16-25" -msgstr "" +msgstr "16-25 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:26 Form/form_us.xml.h:47 Form/form_us.xml.h:69 msgid "Free white males 26-44" -msgstr "" +msgstr "26-44 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:27 Form/form_us.xml.h:48 Form/form_us.xml.h:70 msgid "Free white males 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri özgür beyaz erkekler" #: Form/form_us.xml.h:28 Form/form_us.xml.h:49 Form/form_us.xml.h:71 msgid "Free white females under 10" -msgstr "" +msgstr "10 yaş altı özgür beyaz kadınlar" #: Form/form_us.xml.h:29 Form/form_us.xml.h:50 Form/form_us.xml.h:72 msgid "Free white females 10-15" -msgstr "" +msgstr "10-15 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:30 Form/form_us.xml.h:51 Form/form_us.xml.h:73 msgid "Free white females 16-25" -msgstr "" +msgstr "16-25 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:31 Form/form_us.xml.h:52 Form/form_us.xml.h:74 msgid "Free white females 26-44" -msgstr "" +msgstr "26-44 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:32 Form/form_us.xml.h:53 Form/form_us.xml.h:75 msgid "Free white females 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri özgür beyaz kadınlar" #: Form/form_us.xml.h:67 msgid "Free white males 16-18" -msgstr "" +msgstr "16-18 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:76 Form/form_us.xml.h:1297 msgid "Foreigners not naturalized" -msgstr "" +msgstr "Vatandaşlığa kabul edilmemiş yabancılar" #: Form/form_us.xml.h:77 msgid "Persons engaged in Agriculture" -msgstr "" +msgstr "Tarımda çalışan kişiler" #: Form/form_us.xml.h:78 msgid "Persons engaged in Commerce" -msgstr "" +msgstr "Ticarette çalışan kişiler" #: Form/form_us.xml.h:79 msgid "Persons engaged in Manufactures" -msgstr "" +msgstr "İmalatta çalışan kişiler" #: Form/form_us.xml.h:80 msgid "Slave males under 14" -msgstr "" +msgstr "14 yaş altı köle erkekler" #: Form/form_us.xml.h:81 msgid "Slave males 14-25" -msgstr "" +msgstr "14-25 yaş arası köle erkekler" #: Form/form_us.xml.h:82 msgid "Slave males 26-44" -msgstr "" +msgstr "26-44 yaş arası köle erkekler" #: Form/form_us.xml.h:83 msgid "Slave males 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri köle erkekler" #: Form/form_us.xml.h:84 msgid "Slave females under 14" -msgstr "" +msgstr "14 yaş altı köle kadınlar" #: Form/form_us.xml.h:85 msgid "Slave females 14-25" -msgstr "" +msgstr "14-25 yaş arası köle kadınlar" #: Form/form_us.xml.h:86 msgid "Slave females 26-44" -msgstr "" +msgstr "26-44 yaş arası köle kadınlar" #: Form/form_us.xml.h:87 msgid "Slave females 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri köle kadınlar" #: Form/form_us.xml.h:88 msgid "Free colored males under 14" -msgstr "" +msgstr "14 yaş altı özgür renkli erkekler" #: Form/form_us.xml.h:89 msgid "Free colored males 14-25" -msgstr "" +msgstr "14-25 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:90 msgid "Free colored males 26-44" -msgstr "" +msgstr "26-44 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:91 msgid "Free colored males 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri özgür renkli erkekler" #: Form/form_us.xml.h:92 msgid "Free colored females under 14" -msgstr "" +msgstr "14 yaş altı özgür renkli kadınlar" #: Form/form_us.xml.h:93 msgid "Free colored females 14-25" -msgstr "" +msgstr "14-25 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:94 msgid "Free colored females 26-44" -msgstr "" +msgstr "26-44 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:95 msgid "Free colored females 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri özgür renkli kadınlar" #: Form/form_us.xml.h:105 Form/form_us.xml.h:174 msgid "Free white males under 5" -msgstr "" +msgstr "5 yaş altı özgür beyaz erkekler" #: Form/form_us.xml.h:106 Form/form_us.xml.h:175 msgid "Free white males 5-9" -msgstr "" +msgstr "5-9 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:107 Form/form_us.xml.h:176 msgid "Free white males 10-14" -msgstr "" +msgstr "10-14 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:108 Form/form_us.xml.h:177 msgid "Free white males 15-20" -msgstr "" +msgstr "15-20 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:109 Form/form_us.xml.h:178 msgid "Free white males 20-29" -msgstr "" +msgstr "20-29 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:110 Form/form_us.xml.h:179 msgid "Free white males 30-39" -msgstr "" +msgstr "30-39 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:111 Form/form_us.xml.h:180 msgid "Free white males 40-49" -msgstr "" +msgstr "40-49 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:112 Form/form_us.xml.h:181 msgid "Free white males 50-59" -msgstr "" +msgstr "50-59 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:113 Form/form_us.xml.h:182 msgid "Free white males 60-69" -msgstr "" +msgstr "60-69 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:114 Form/form_us.xml.h:183 msgid "Free white males 70-79" -msgstr "" +msgstr "70-79 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:115 Form/form_us.xml.h:184 msgid "Free white males 80-89" -msgstr "" +msgstr "80-89 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:116 Form/form_us.xml.h:185 msgid "Free white males 90-99" -msgstr "" +msgstr "90-99 yaş arası özgür beyaz erkekler" #: Form/form_us.xml.h:117 Form/form_us.xml.h:186 msgid "Free white males 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri özgür beyaz erkekler" #: Form/form_us.xml.h:118 Form/form_us.xml.h:187 msgid "Free white females under 5" -msgstr "" +msgstr "5 yaş altı özgür beyaz kadınlar" #: Form/form_us.xml.h:119 Form/form_us.xml.h:188 msgid "Free white females 5-9" -msgstr "" +msgstr "5-9 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:120 Form/form_us.xml.h:189 msgid "Free white females 10-14" -msgstr "" +msgstr "10-14 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:121 Form/form_us.xml.h:190 msgid "Free white females 15-20" -msgstr "" +msgstr "5-20 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:122 Form/form_us.xml.h:191 msgid "Free white females 20-29" -msgstr "" +msgstr "20-29 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:123 Form/form_us.xml.h:192 msgid "Free white females 30-39" -msgstr "" +msgstr "30-39 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:124 Form/form_us.xml.h:193 msgid "Free white females 40-49" -msgstr "" +msgstr "40-49 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:125 Form/form_us.xml.h:194 msgid "Free white females 50-59" -msgstr "" +msgstr "50-59 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:126 Form/form_us.xml.h:195 msgid "Free white females 60-69" -msgstr "" +msgstr "60-69 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:127 Form/form_us.xml.h:196 msgid "Free white females 70-79" -msgstr "" +msgstr "70-79 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:128 Form/form_us.xml.h:197 msgid "Free white females 80-89" -msgstr "" +msgstr "80-89 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:129 Form/form_us.xml.h:198 msgid "Free white females 90-99" -msgstr "" +msgstr "90-99 yaş arası özgür beyaz kadınlar" #: Form/form_us.xml.h:130 Form/form_us.xml.h:199 msgid "Free white females 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri özgür beyaz kadınlar" #: Form/form_us.xml.h:131 Form/form_us.xml.h:212 msgid "Slave males under 10" -msgstr "" +msgstr "10 yaş altı köle erkekler" #: Form/form_us.xml.h:132 Form/form_us.xml.h:213 msgid "Slave males 10-23" -msgstr "" +msgstr "10-23 yaş arası köle erkekler" #: Form/form_us.xml.h:133 Form/form_us.xml.h:214 msgid "Slave males 24-35" -msgstr "" +msgstr "24-35 yaş arası köle erkekler" #: Form/form_us.xml.h:134 Form/form_us.xml.h:215 msgid "Slave males 36-54" -msgstr "" +msgstr "36-54 yaş arası köle erkekler" #: Form/form_us.xml.h:135 Form/form_us.xml.h:216 msgid "Slave males 55-99" -msgstr "" +msgstr "55-99 yaş arası köle erkekler" #: Form/form_us.xml.h:136 Form/form_us.xml.h:217 msgid "Slave males 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri köle erkekler" #: Form/form_us.xml.h:137 Form/form_us.xml.h:218 msgid "Slave females under 10" -msgstr "" +msgstr "10 yaş altı köle kadınlar" #: Form/form_us.xml.h:138 Form/form_us.xml.h:219 msgid "Slave females 10-23" -msgstr "" +msgstr "10-23 yaş arası köle kadınlar" #: Form/form_us.xml.h:139 Form/form_us.xml.h:220 msgid "Slave females 24-35" -msgstr "" +msgstr "24-35 yaş arası köle kadınlar" #: Form/form_us.xml.h:140 Form/form_us.xml.h:221 msgid "Slave females 36-54" -msgstr "" +msgstr "36-54 yaş arası köle kadınlar" #: Form/form_us.xml.h:141 Form/form_us.xml.h:222 msgid "Slave females 55-99" -msgstr "" +msgstr "55-99 yaş arası köle kadınlar" #: Form/form_us.xml.h:142 Form/form_us.xml.h:223 msgid "Slave females 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri köle kadınlar" #: Form/form_us.xml.h:143 Form/form_us.xml.h:200 msgid "Free colored males under 10" -msgstr "" +msgstr "10 yaş altı özgür renkli erkekler" #: Form/form_us.xml.h:144 Form/form_us.xml.h:201 msgid "Free colored males 10-23" -msgstr "" +msgstr "10-23 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:145 Form/form_us.xml.h:202 msgid "Free colored males 24-35" -msgstr "" +msgstr "24-35 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:146 Form/form_us.xml.h:203 msgid "Free colored males 36-54" -msgstr "" +msgstr "36-54 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:147 Form/form_us.xml.h:204 msgid "Free colored males 55-99" -msgstr "" +msgstr "55-99 yaş arası özgür renkli erkekler" #: Form/form_us.xml.h:148 Form/form_us.xml.h:205 msgid "Free colored males 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri özgür renkli erkekler" #: Form/form_us.xml.h:149 Form/form_us.xml.h:206 msgid "Free colored females under 10" -msgstr "" +msgstr "10 yaş altı özgür renkli kadınlar" #: Form/form_us.xml.h:150 Form/form_us.xml.h:207 msgid "Free colored females 10-23" -msgstr "" +msgstr "10-23 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:151 Form/form_us.xml.h:208 msgid "Free colored females 24-35" -msgstr "" +msgstr "24-35 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:152 Form/form_us.xml.h:209 msgid "Free colored females 36-54" -msgstr "" +msgstr "36-54 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:153 Form/form_us.xml.h:210 msgid "Free colored females 55-99" -msgstr "" +msgstr "55-99 yaş arası özgür renkli kadınlar" #: Form/form_us.xml.h:154 Form/form_us.xml.h:211 msgid "Free colored females 100 and over" -msgstr "" +msgstr "100 yaş ve üzeri özgür renkli kadınlar" #: Form/form_us.xml.h:156 Form/form_us.xml.h:234 msgid "White persons deaf/dumb under 14" -msgstr "" +msgstr "14 yaş altı sağır/dilsiz beyaz kişiler" #: Form/form_us.xml.h:157 Form/form_us.xml.h:235 msgid "White persons deaf/dumb 14-24" -msgstr "" +msgstr "14-24 yaş arası sağır/dilsiz beyaz kişiler" #: Form/form_us.xml.h:158 Form/form_us.xml.h:236 msgid "White persons deaf/dumb 25 and over" -msgstr "" +msgstr "25 yaş ve üzeri sağır/dilsiz beyaz kişiler" #: Form/form_us.xml.h:159 Form/form_us.xml.h:237 msgid "White persons blind" -msgstr "" +msgstr "Kör beyaz kişiler" #: Form/form_us.xml.h:160 Form/form_us.xml.h:1211 msgid "Aliens" -msgstr "" +msgstr "Yabancılar" #: Form/form_us.xml.h:161 msgid "Slaves and colored persons deaf/dumb under 14" -msgstr "" +msgstr "14 yaş altı sağır/dilsiz köleler ve renkli kişiler" #: Form/form_us.xml.h:162 msgid "Slaves and colored persons deaf/dumb 14-24" -msgstr "" +msgstr "14-24 yaş arası köleler ve renkli sağır/dilsiz kişiler" #: Form/form_us.xml.h:163 msgid "Slaves and colored persons deaf/dumb 25 and over" -msgstr "" +msgstr "25 yaş ve üzeri köleler ve renkli sağır/dilsiz kişiler" #: Form/form_us.xml.h:164 msgid "Slaves and colored persons blind" -msgstr "" +msgstr "Köleler ve renkli kör kişiler" #: Form/form_us.xml.h:225 msgid "Persons employed in Mining" -msgstr "" +msgstr "Madencilikte çalışan kişiler" #: Form/form_us.xml.h:226 msgid "Persons employed in Agriculture" -msgstr "" +msgstr "Tarımda çalışan kişiler" #: Form/form_us.xml.h:227 msgid "Persons employed in Commerce" -msgstr "" +msgstr "Ticarette çalışan kişiler" #: Form/form_us.xml.h:228 msgid "Persons employed in Manufacture and trade" -msgstr "" +msgstr "İmalat ve ticarette çalışan kişiler" #: Form/form_us.xml.h:229 msgid "Persons employed in Navigation of the ocean" -msgstr "" +msgstr "Okyanusta seyrüseferde çalışan kişiler" #: Form/form_us.xml.h:230 msgid "Persons employed in Navigation of canals, lakes, rivers" -msgstr "" +msgstr "Kanallarda, göllerde, nehirlerde seyrüseferde çalışan kişiler" #: Form/form_us.xml.h:231 msgid "Learned professional engineers" -msgstr "" +msgstr "Eğitimli profesyonel mühendisler" #: Form/form_us.xml.h:232 msgid "Names of pensioners for Revolutionary or military services" -msgstr "" +msgstr "Devrimci savaş veya askerî hizmetler için emekli maaşı alanların adları" #: Form/form_us.xml.h:233 msgid "Ages" -msgstr "" +msgstr "Yaşlar" #: Form/form_us.xml.h:238 msgid "White persons insane and idiots at public charge" -msgstr "" +msgstr "Kamu desteğiyle yaşayan beyaz akıl hastaları ve zekâ geriliği olanlar" #: Form/form_us.xml.h:239 msgid "White persons insane and idiots at private charge" -msgstr "" +msgstr "Özel destekle yaşayan beyaz akıl hastaları ve zekâ geriliği olanlar" #: Form/form_us.xml.h:240 msgid "Colored persons deaf/dumb" -msgstr "" +msgstr "Sağır/dilsiz renkli kişiler" #: Form/form_us.xml.h:241 msgid "Colored persons blind" -msgstr "" +msgstr "Kör renkli kişiler" #: Form/form_us.xml.h:242 msgid "Colored persons insane and idiots at private charge" msgstr "" +"Özel destekle yaşayan akıl hastaları ve zekâ geriliği olan renkli kişiler" #: Form/form_us.xml.h:243 msgid "Colored persons insane and idiots at public charge" msgstr "" +"Kamu desteğiyle yaşayan akıl hastaları ve zekâ geriliği olan renkli kişiler" #: Form/form_us.xml.h:244 msgid "Universities or college" -msgstr "" +msgstr "Üniversiteler veya kolejler" #: Form/form_us.xml.h:245 msgid "Number of students" -msgstr "" +msgstr "Öğrenci sayısı" #: Form/form_us.xml.h:246 msgid "Academies and Grammar Schools" -msgstr "" +msgstr "Akademiler ve Gramer Okulları" #: Form/form_us.xml.h:247 msgid "No. of scholars" -msgstr "" +msgstr "Öğrenci sayısı" #: Form/form_us.xml.h:248 msgid "Primary and Common Schools" -msgstr "" +msgstr "İlkokullar ve Ortaokullar" #: Form/form_us.xml.h:249 msgid "No. of scholars at public charge" -msgstr "" +msgstr "Kamu desteğiyle eğitim gören öğrenci sayısı" #: Form/form_us.xml.h:250 msgid "White persons illiterate over 20" -msgstr "" +msgstr "20 yaş üzeri okuma yazma bilmeyen beyaz kişiler" #: Form/form_us.xml.h:257 Form/form_us.xml.h:278 Form/form_us.xml.h:332 #: Form/form_us.xml.h:354 Form/form_us.xml.h:412 Form/form_us.xml.h:431 @@ -10825,7 +10857,7 @@ msgstr "" #: Form/form_us.xml.h:647 Form/form_us.xml.h:783 Form/form_us.xml.h:826 #: Form/form_us.xml.h:1067 Form/form_us.xml.h:1100 Form/form_us.xml.h:2505 msgid "State of" -msgstr "" +msgstr "Eyaleti" #: Form/form_us.xml.h:259 Form/form_us.xml.h:280 Form/form_us.xml.h:335 #: Form/form_us.xml.h:356 Form/form_us.xml.h:413 Form/form_us.xml.h:434 @@ -10834,17 +10866,17 @@ msgstr "" #: Form/form_us.xml.h:1847 Form/form_us.xml.h:2372 Form/form_us.xml.h:2394 #: Form/form_us.xml.h:2486 Form/form_us.xml.h:2507 msgid "Line No." -msgstr "" +msgstr "Satır No." #: Form/form_us.xml.h:260 Form/form_us.xml.h:336 Form/form_us.xml.h:435 #: Form/form_us.xml.h:465 Form/form_us.xml.h:615 Form/form_us.xml.h:1105 #: Form/form_us.xml.h:1848 msgid "Family No." -msgstr "" +msgstr "Aile No." #: Form/form_us.xml.h:266 Form/form_us.xml.h:342 Form/form_us.xml.h:441 msgid "Value of real estate" -msgstr "" +msgstr "Gayrimenkul değeri" #: Form/form_us.xml.h:267 Form/form_us.xml.h:344 Form/form_us.xml.h:420 #: Form/form_us.xml.h:443 Form/form_us.xml.h:487 Form/form_us.xml.h:625 @@ -10861,466 +10893,469 @@ msgstr "Doğum yeri" #: Form/form_us.xml.h:268 Form/form_us.xml.h:345 msgid "Married within the year" -msgstr "" +msgstr "Yıl içinde evlenen" #: Form/form_us.xml.h:269 Form/form_us.xml.h:346 Form/form_us.xml.h:448 #: Form/form_us.xml.h:1546 Form/form_us.xml.h:1568 Form/form_us.xml.h:1590 msgid "Attended school within the year" -msgstr "" +msgstr "Yıl içinde okula devam eden" #: Form/form_us.xml.h:270 Form/form_us.xml.h:347 msgid "Illiterate and over 20" -msgstr "" +msgstr "Okuma yazma bilmeyen ve 20 yaş üzeri" #: Form/form_us.xml.h:271 Form/form_us.xml.h:348 msgid "Deaf, dumb, blind, insane, idiotic, pauper or convict" msgstr "" +"Sağır, dilsiz, kör, akıl hastası, zekâ geriliği olan, yoksul veya hükümlü" #: Form/form_us.xml.h:276 Form/form_us.xml.h:352 msgid "Production of Agriculture in" -msgstr "" +msgstr "Tarım Üretimi" #: Form/form_us.xml.h:277 Form/form_us.xml.h:353 Form/form_us.xml.h:411 msgid "in the County of" -msgstr "" +msgstr "İlçesinde" #: Form/form_us.xml.h:281 Form/form_us.xml.h:358 msgid "Owner, Agent, or Manager" -msgstr "" +msgstr "Sahip, Temsilci veya Yönetici" #: Form/form_us.xml.h:282 Form/form_us.xml.h:359 msgid "Acres Improved" -msgstr "" +msgstr "İyileştirilmiş dönüm alanı" #: Form/form_us.xml.h:283 Form/form_us.xml.h:360 msgid "Acres Unimproved" -msgstr "" +msgstr "İyileştirilmemiş dönüm alanı" #: Form/form_us.xml.h:284 Form/form_us.xml.h:361 msgid "Cash Value of Farm" -msgstr "" +msgstr "Çiftliğin Nakit Değeri" #: Form/form_us.xml.h:285 Form/form_us.xml.h:362 msgid "Value of Farming Implements and Machinery" -msgstr "" +msgstr "Tarım Aletleri ve Makinelerinin Değeri" #: Form/form_us.xml.h:287 Form/form_us.xml.h:365 msgid "Asses and Mules" -msgstr "" +msgstr "Eşekler ve Katırlar" #: Form/form_us.xml.h:289 Form/form_us.xml.h:367 msgid "Working Oxen" -msgstr "" +msgstr "Çalıştırılan Öküzler" #: Form/form_us.xml.h:290 Form/form_us.xml.h:368 msgid "Other Cattle" -msgstr "" +msgstr "Diğer Sığırlar" #: Form/form_us.xml.h:292 Form/form_us.xml.h:370 msgid "Swine" -msgstr "" +msgstr "Domuzlar" #: Form/form_us.xml.h:293 Form/form_us.xml.h:371 Form/form_us.xml.h:511 #: Form/form_us.xml.h:1186 msgid "Value of Live Stock" -msgstr "" +msgstr "Canlı Hayvan Değeri" #: Form/form_us.xml.h:294 msgid "Wheat" -msgstr "" +msgstr "Buğday" #: Form/form_us.xml.h:295 msgid "Indian Corn" -msgstr "" +msgstr "Hint Mısırı" #: Form/form_us.xml.h:296 msgid "Oats" -msgstr "" +msgstr "Yulaf" #: Form/form_us.xml.h:297 msgid "Rice" -msgstr "" +msgstr "Pirinç" #: Form/form_us.xml.h:298 msgid "Tobacco" -msgstr "" +msgstr "Tütün" #: Form/form_us.xml.h:299 msgid "Ginned Cotton" -msgstr "" +msgstr "Çırçırlanmış Pamuk" #: Form/form_us.xml.h:300 msgid "Wool" -msgstr "" +msgstr "Yün" #: Form/form_us.xml.h:301 msgid "Peas and Beans" -msgstr "" +msgstr "Bezelye ve Fasulye" #: Form/form_us.xml.h:302 msgid "Irish Potatoes" -msgstr "" +msgstr "İrlanda Patatesi" #: Form/form_us.xml.h:303 msgid "Sweet Potatoes" -msgstr "" +msgstr "Tatlı Patates" #: Form/form_us.xml.h:304 msgid "Barley" -msgstr "" +msgstr "Arpa" #: Form/form_us.xml.h:305 msgid "Buckwheat" -msgstr "" +msgstr "Karabuğday" #: Form/form_us.xml.h:306 msgid "Value of Orchard Products" -msgstr "" +msgstr "Meyve Bahçesi Ürünlerinin Değeri" #: Form/form_us.xml.h:307 msgid "Wine" -msgstr "" +msgstr "Şarap" #: Form/form_us.xml.h:308 msgid "Value of Market Gardens" -msgstr "" +msgstr "Pazar Bahçelerinin Değeri" #: Form/form_us.xml.h:309 msgid "Butter" -msgstr "" +msgstr "Tereyağı" #: Form/form_us.xml.h:310 msgid "Cheese" -msgstr "" +msgstr "Peynir" #: Form/form_us.xml.h:311 msgid "Hay" -msgstr "" +msgstr "Saman" #: Form/form_us.xml.h:312 msgid "Clover Seed" -msgstr "" +msgstr "Yonca Tohumu" #: Form/form_us.xml.h:313 msgid "Other Grass Seeds" -msgstr "" +msgstr "Diğer Çim Tohumları" #: Form/form_us.xml.h:314 msgid "Hops" -msgstr "" +msgstr "Şerbetçiotu" #: Form/form_us.xml.h:315 msgid "Hemp Dew Rotted" -msgstr "" +msgstr "Çiğde Çürütülmüş Kenevir" #: Form/form_us.xml.h:316 msgid "Hemp Water Rotted" -msgstr "" +msgstr "Suda Çürütülmüş Kenevir" #: Form/form_us.xml.h:317 msgid "Flax" -msgstr "" +msgstr "Keten" #: Form/form_us.xml.h:318 msgid "Flaxseed" -msgstr "" +msgstr "Keten Tohumu" #: Form/form_us.xml.h:319 msgid "Silk Cocoons" -msgstr "" +msgstr "İpek Kozaları" #: Form/form_us.xml.h:320 msgid "Maple Sugar" -msgstr "" +msgstr "Akçaağaç Şekeri" #: Form/form_us.xml.h:321 msgid "Cane Sugar" -msgstr "" +msgstr "Kamış Şekeri" #: Form/form_us.xml.h:322 msgid "Molasses" -msgstr "" +msgstr "Pekmez" #: Form/form_us.xml.h:323 msgid "Beeswax" -msgstr "" +msgstr "Balmumu" #: Form/form_us.xml.h:324 msgid "Value of Homemade Manufactures" -msgstr "" +msgstr "Ev Yapımı İmalatların Değeri" #: Form/form_us.xml.h:325 Form/form_us.xml.h:406 msgid "Value of Animals Slaughtered" -msgstr "" +msgstr "Kesilen Hayvanların Değeri" #: Form/form_us.xml.h:334 Form/form_us.xml.h:357 Form/form_us.xml.h:433 #: Form/form_us.xml.h:655 Form/form_us.xml.h:1380 Form/form_us.xml.h:1482 #: Form/form_us.xml.h:1637 Form/form_us.xml.h:1652 Form/form_us.xml.h:2295 #: Form/form_us.xml.h:2324 msgid "Post Office" -msgstr "" +msgstr "Postane" #: Form/form_us.xml.h:343 Form/form_us.xml.h:442 msgid "Value of personal estate" -msgstr "" +msgstr "Kişisel mülk değeri" #: Form/form_us.xml.h:363 msgid "Live Stock June 1, 1860" -msgstr "" +msgstr "1 Haziran 1860 Tarihi İtibariyle Canlı Hayvanlar" #: Form/form_us.xml.h:372 msgid "Produce during the year ending June 1, 1860" -msgstr "" +msgstr "1 Haziran 1860 tarihinde sona eren yıl içindeki ürünler" #: Form/form_us.xml.h:373 msgid "Wheat, bushels of" -msgstr "" +msgstr "Buğday, kile olarak" #: Form/form_us.xml.h:374 msgid "Rye, bushels of" -msgstr "" +msgstr "Çavdar, kile olarak" #: Form/form_us.xml.h:375 msgid "Indian Corn, bushels of" -msgstr "" +msgstr "Hint Mısırı, kile olarak" #: Form/form_us.xml.h:376 msgid "Oats, bushels of" -msgstr "" +msgstr "Yulaf, kile olarak" #: Form/form_us.xml.h:377 msgid "Rice, pounds of" -msgstr "" +msgstr "Pirinç, pound olarak" #: Form/form_us.xml.h:378 msgid "Tobacco, pounds of" -msgstr "" +msgstr "Tütün, pound olarak" #: Form/form_us.xml.h:379 msgid "Ginned Cotton, bales of 400 lbs." -msgstr "" +msgstr "Çırçırlanmış Pamuk, 400 poundluk balyalar halinde" #: Form/form_us.xml.h:380 msgid "Wool, pounds of" -msgstr "" +msgstr "Yün, pound olarak" #: Form/form_us.xml.h:381 msgid "Peas and Beans, bushels of" -msgstr "" +msgstr "Bezelye ve Fasulye, kile olarak" #: Form/form_us.xml.h:382 msgid "Irish Potatoes, bushels of" -msgstr "" +msgstr "İrlanda Patatesi, kile olarak" #: Form/form_us.xml.h:383 msgid "Sweet Potatoes, bushels of" -msgstr "" +msgstr "Tatlı Patates, kile olarak" #: Form/form_us.xml.h:384 msgid "Barley, bushels of" -msgstr "" +msgstr "Arpa, kile olarak" #: Form/form_us.xml.h:385 msgid "Buckwheat, bushels of" -msgstr "" +msgstr "Karabuğday, kile olarak" #: Form/form_us.xml.h:386 msgid "Value of Orchard Products in $" -msgstr "" +msgstr "Meyve Bahçesi Ürünlerinin Değeri, $ olarak" #: Form/form_us.xml.h:387 msgid "Wine, gallons of" -msgstr "" +msgstr "Şarap, galon olarak" #: Form/form_us.xml.h:388 msgid "Value of products of market gardens" -msgstr "" +msgstr "Pazar bahçesi ürünlerinin değeri" #: Form/form_us.xml.h:389 msgid "Butter, pounds of" -msgstr "" +msgstr "Tereyağı, pound olarak" #: Form/form_us.xml.h:390 msgid "Cheese, pounds of" -msgstr "" +msgstr "Peynir, pound olarak" #: Form/form_us.xml.h:391 msgid "Hay, tons of" -msgstr "" +msgstr "Saman, ton olarak" #: Form/form_us.xml.h:392 msgid "Clover Seed, bushels of" -msgstr "" +msgstr "Yonca Tohumu, kile olarak" #: Form/form_us.xml.h:393 msgid "Other Grass Seeds, bushels of" -msgstr "" +msgstr "Diğer Çim Tohumları, kile olarak" #: Form/form_us.xml.h:394 msgid "Hops, pounds of" -msgstr "" +msgstr "Şerbetçiotu, pound olarak" #: Form/form_us.xml.h:395 msgid "Hemp Dew Rotted, tons of" -msgstr "" +msgstr "Çiğde Çürütülmüş Kenevir, ton olarak" #: Form/form_us.xml.h:396 msgid "Hemp Water Rotted, tons of" -msgstr "" +msgstr "Suda Çürütülmüş Kenevir, ton olarak" #: Form/form_us.xml.h:397 msgid "Flax, pounds of" -msgstr "" +msgstr "Keten, pound olarak" #: Form/form_us.xml.h:398 msgid "Flaxseed, bushels of" -msgstr "" +msgstr "Keten tohumu, kile olarak" #: Form/form_us.xml.h:399 msgid "Silk Cocoons, pounds of" -msgstr "" +msgstr "İpek Kozası, pound olarak" #: Form/form_us.xml.h:400 msgid "Maple Sugar, pounds of" -msgstr "" +msgstr "Akçaağaç Şekeri, pound olarak" #: Form/form_us.xml.h:401 msgid "Cane sugar, hogshead of 1,000 pounds" -msgstr "" +msgstr "Kamış şekeri, 1.000 poundluk fıçı olarak" #: Form/form_us.xml.h:402 msgid "Molasses, gallons of" -msgstr "" +msgstr "Pekmez, galon olarak" #: Form/form_us.xml.h:403 msgid "Beeswax, pounds of" -msgstr "" +msgstr "Balmumu, pound olarak" #: Form/form_us.xml.h:404 msgid "Honey, pounds of" -msgstr "" +msgstr "Bal, pound olarak" #: Form/form_us.xml.h:405 msgid "Value of home-made manufactures" -msgstr "" +msgstr "Ev yapımı imalatların değeri" #: Form/form_us.xml.h:410 msgid "Persons who Died during the Year ending 1 June 1860 in" -msgstr "" +msgstr "1 Haziran 1860'ta sona eren yıl içinde ölen kişiler" #: Form/form_us.xml.h:418 msgid "Free or slave" -msgstr "" +msgstr "Özgür veya köle" #: Form/form_us.xml.h:419 msgid "Married or widowed" -msgstr "" +msgstr "Evli veya dul" #: Form/form_us.xml.h:421 msgid "Month in which person died" -msgstr "" +msgstr "Kişinin öldüğü ay" #: Form/form_us.xml.h:423 msgid "Cause of death" -msgstr "" +msgstr "Ölüm nedeni" #: Form/form_us.xml.h:424 msgid "Number of days ill" -msgstr "" +msgstr "Hasta olunan gün sayısı" #: Form/form_us.xml.h:444 Form/form_us.xml.h:1643 msgid "Father of foreign birth" -msgstr "" +msgstr "Babanın yabancı doğumlu olması" #: Form/form_us.xml.h:445 Form/form_us.xml.h:1644 msgid "Mother of foreign birth" -msgstr "" +msgstr "Annenin yabancı doğumlu olması" #: Form/form_us.xml.h:446 msgid "Month, if born within the year" -msgstr "" +msgstr "Yıl içinde doğmuşsa, doğduğu ay" #: Form/form_us.xml.h:447 msgid "Month, if married within the year" -msgstr "" +msgstr "Yıl içinde evlenmişse, evlendiği ay" #: Form/form_us.xml.h:449 Form/form_us.xml.h:485 Form/form_us.xml.h:1125 #: Form/form_us.xml.h:1868 msgid "Cannot read" -msgstr "" +msgstr "Okuyamıyor" #: Form/form_us.xml.h:450 Form/form_us.xml.h:486 Form/form_us.xml.h:1126 #: Form/form_us.xml.h:1869 msgid "Cannot write" -msgstr "" +msgstr "Yazamıyor" #: Form/form_us.xml.h:451 msgid "Deaf, dumb, blind, insane or idiotic" -msgstr "" +msgstr "Sağır, dilsiz, kör, akıl hastası veya zihinsel engelli" #: Form/form_us.xml.h:452 msgid "Male citizen 21 and over" -msgstr "" +msgstr "21 yaş ve üzeri erkek vatandaş" #: Form/form_us.xml.h:453 msgid "" "Male citizen 21 and over where right to vote is denied on grounds other than " "rebellion or other crime" msgstr "" +"21 yaş ve üzeri, isyan veya başka bir suç dışında nedenlerle oy kullanma " +"hakkı elinden alınmış erkek vatandaş" #: Form/form_us.xml.h:458 Form/form_us.xml.h:494 Form/form_us.xml.h:606 #: Form/form_us.xml.h:662 Form/form_us.xml.h:704 Form/form_us.xml.h:747 #: Form/form_us.xml.h:790 Form/form_us.xml.h:833 Form/form_us.xml.h:1096 msgid "Supervisor's Dist. No." -msgstr "" +msgstr "Denetmen Bölgesi No." #: Form/form_us.xml.h:459 Form/form_us.xml.h:495 Form/form_us.xml.h:607 #: Form/form_us.xml.h:663 Form/form_us.xml.h:705 Form/form_us.xml.h:748 #: Form/form_us.xml.h:791 Form/form_us.xml.h:834 Form/form_us.xml.h:1097 #: Form/form_us.xml.h:1843 msgid "Enumeration Dist. No." -msgstr "" +msgstr "Sayım Bölgesi No." #: Form/form_us.xml.h:470 Form/form_us.xml.h:1110 Form/form_us.xml.h:1853 msgid "Month, if born within the census year" -msgstr "" +msgstr "Sayım yılı içinde doğmuşsa, doğduğu ay" #: Form/form_us.xml.h:471 Form/form_us.xml.h:618 Form/form_us.xml.h:714 #: Form/form_us.xml.h:757 Form/form_us.xml.h:797 Form/form_us.xml.h:1075 #: Form/form_us.xml.h:1111 Form/form_us.xml.h:1854 Form/form_us.xml.h:1884 #: Form/form_us.xml.h:1906 Form/form_us.xml.h:1935 Form/form_us.xml.h:2331 msgid "Relationship to head" -msgstr "" +msgstr "Aile reisiyle ilişkisi" #: Form/form_us.xml.h:472 Form/form_us.xml.h:1112 Form/form_us.xml.h:1522 #: Form/form_us.xml.h:1855 Form/form_us.xml.h:1912 Form/form_us.xml.h:1939 #: Form/form_us.xml.h:2064 Form/form_us.xml.h:2100 Form/form_us.xml.h:2138 #: Form/form_us.xml.h:2176 Form/form_us.xml.h:2214 msgid "Single" -msgstr "" +msgstr "Bekâr" #: Form/form_us.xml.h:474 Form/form_us.xml.h:1114 Form/form_us.xml.h:1857 msgid "Widowed, divorced" -msgstr "" +msgstr "Dul, boşanmış" #: Form/form_us.xml.h:475 Form/form_us.xml.h:623 Form/form_us.xml.h:1115 #: Form/form_us.xml.h:1858 msgid "Married during the census year" -msgstr "" +msgstr "Sayım yılı içinde evlenmiş" #: Form/form_us.xml.h:477 Form/form_us.xml.h:632 Form/form_us.xml.h:1117 #: Form/form_us.xml.h:1860 msgid "Number of months unemployed during the census year" -msgstr "" +msgstr "Sayım yılı içinde işsiz kalınan ay sayısı" #: Form/form_us.xml.h:478 Form/form_us.xml.h:1118 Form/form_us.xml.h:1861 msgid "Illness on day of enumeration" -msgstr "" +msgstr "Sayım gününde hastalık durumu" #: Form/form_us.xml.h:480 Form/form_us.xml.h:742 Form/form_us.xml.h:1120 #: Form/form_us.xml.h:1863 msgid "Deaf and dumb" -msgstr "" +msgstr "Sağır ve dilsiz" #: Form/form_us.xml.h:482 Form/form_us.xml.h:1084 Form/form_us.xml.h:1122 #: Form/form_us.xml.h:1215 Form/form_us.xml.h:1418 Form/form_us.xml.h:1865 @@ -11328,425 +11363,428 @@ msgstr "" #: Form/form_us.xml.h:2183 Form/form_us.xml.h:2221 Form/form_us.xml.h:2243 #: Form/form_us.xml.h:2255 Form/form_us.xml.h:2267 msgid "Insane" -msgstr "" +msgstr "Akıl hastası" #: Form/form_us.xml.h:483 Form/form_us.xml.h:1123 Form/form_us.xml.h:1866 msgid "Maimed, crippled, bedridden, or otherwise disabled" -msgstr "" +msgstr "Sakat, engelli, yatalak veya başka şekilde engelli" #: Form/form_us.xml.h:484 Form/form_us.xml.h:1124 Form/form_us.xml.h:1867 msgid "Attended school within the census year" -msgstr "" +msgstr "Sayım yılı içinde okula devam etti" #: Form/form_us.xml.h:488 Form/form_us.xml.h:626 Form/form_us.xml.h:684 #: Form/form_us.xml.h:723 Form/form_us.xml.h:772 Form/form_us.xml.h:810 #: Form/form_us.xml.h:870 Form/form_us.xml.h:936 Form/form_us.xml.h:1090 #: Form/form_us.xml.h:1128 Form/form_us.xml.h:1871 msgid "Place of birth of father" -msgstr "" +msgstr "Babanın doğum yeri" #: Form/form_us.xml.h:489 Form/form_us.xml.h:627 Form/form_us.xml.h:685 #: Form/form_us.xml.h:724 Form/form_us.xml.h:774 Form/form_us.xml.h:811 #: Form/form_us.xml.h:871 Form/form_us.xml.h:938 Form/form_us.xml.h:1091 #: Form/form_us.xml.h:1129 Form/form_us.xml.h:1872 msgid "Place of birth of mother" -msgstr "" +msgstr "Annenin doğum yeri" #: Form/form_us.xml.h:502 msgid "Owner" -msgstr "" +msgstr "Sahip" #: Form/form_us.xml.h:503 msgid "Rents for fixed money" -msgstr "" +msgstr "Sabit gelir karşılığında alınan kiralar" #: Form/form_us.xml.h:504 msgid "Rents for share of products" -msgstr "" +msgstr "Ürün payı karşılığında alınan kiralar" #: Form/form_us.xml.h:505 msgid "Acres Tilled" -msgstr "" +msgstr "İşlenen dönüm" #: Form/form_us.xml.h:506 msgid "Acres Permanent Meadows, Pastures, Orchards, Vineyards" -msgstr "" +msgstr "Kalıcı çayırlar, meralar, meyve bahçeleri, bağlar, dönüm olarak" #: Form/form_us.xml.h:507 msgid "Acres Woodland and forest" -msgstr "" +msgstr "Ormanlık ve ağaçlık alan, dönüm olarak" #: Form/form_us.xml.h:508 msgid "Acres Other unimproved land" -msgstr "" +msgstr "Diğer işlenmemiş arazi, dönüm olarak" #: Form/form_us.xml.h:509 msgid "Value of Farm" -msgstr "" +msgstr "Çiftliğin değeri" #: Form/form_us.xml.h:510 msgid "Value of Implements and Machinery" -msgstr "" +msgstr "Alet ve makinelerin değeri" #: Form/form_us.xml.h:512 msgid "Cost of building and repairing fences in 1879" -msgstr "" +msgstr "1879 yılında çitlerin yapımı ve onarım maliyeti" #: Form/form_us.xml.h:513 msgid "Cost of Fertilizers purchased in 1879" -msgstr "" +msgstr "1879 yılında satın alınan gübrelerin maliyeti" #: Form/form_us.xml.h:514 msgid "Farm Labor Hiring 1879" -msgstr "" +msgstr "1879 yılında işe alınan tarım işçiliği" #: Form/form_us.xml.h:515 msgid "Weeks hired labor in 1879" -msgstr "" +msgstr "1879 yılında işe alınan işçiliğin hafta sayısı" #: Form/form_us.xml.h:516 msgid "Estimated value of all farm productions for 1879" -msgstr "" +msgstr "1879 yılına ait tüm çiftlik üretiminin tahmini değeri" #: Form/form_us.xml.h:517 msgid "Acreage Grass Lands Mown in 1879" -msgstr "" +msgstr "1879 yılında biçilen çayır arazisi, dönüm olarak" #: Form/form_us.xml.h:518 msgid "Acreage Grass Lands Not Mown in 1879" -msgstr "" +msgstr "1879 yılında biçilmeyen çayır arazisi, dönüm olarak" #: Form/form_us.xml.h:519 msgid "Tons Hay Harvested in 1879" -msgstr "" +msgstr "1879 yılında hasat edilen saman, ton olarak" #: Form/form_us.xml.h:520 msgid "Bushels Clover Seed Harvested in 1879" -msgstr "" +msgstr "1879 yılında hasat edilen yonca tohumu, kile olarak" #: Form/form_us.xml.h:521 msgid "Bushels Grass Seed Harvested in 1879" -msgstr "" +msgstr "1879 yılında hasat edilen çim tohumu, kile olarak" #: Form/form_us.xml.h:522 msgid "Horses on hand" -msgstr "" +msgstr "Eldeki at sayısı" #: Form/form_us.xml.h:523 msgid "Mules and Asses on hand" -msgstr "" +msgstr "Eldeki katır ve eşek sayısı" #: Form/form_us.xml.h:524 msgid "Working Oxen on hand" -msgstr "" +msgstr "Eldeki iş öküzü sayısı" #: Form/form_us.xml.h:525 msgid "Milch Cows on hand" -msgstr "" +msgstr "Eldeki süt ineği sayısı" #: Form/form_us.xml.h:526 msgid "Other Cattle on hand" -msgstr "" +msgstr "Eldeki diğer sığır sayısı" #: Form/form_us.xml.h:527 msgid "Calves Dropped in 1879" -msgstr "" +msgstr "1879 yılında doğan buzağı sayısı" #: Form/form_us.xml.h:528 msgid "Cattle Purchased in 1879" -msgstr "" +msgstr "1879 yılında satın alınan sığır sayısı" #: Form/form_us.xml.h:529 msgid "Cattle Sold living in 1879" -msgstr "" +msgstr "1879 yılında canlı olarak satılan sığır sayısı" #: Form/form_us.xml.h:530 msgid "Cattle Slaughtered in 1879" -msgstr "" +msgstr "1879 yılında kesilen sığır sayısı" #: Form/form_us.xml.h:531 msgid "Cattle Died, strayed, or stolen in 1879" -msgstr "" +msgstr "1879 yılında ölen, kaybolan veya çalınan sığır sayısı" #: Form/form_us.xml.h:532 msgid "Gallons Milk Sold or sent to butter or cheese factories in 1879" msgstr "" +"1879 yılında satılan veya tereyağı ya da peynir fabrikalarına gönderilen " +"süt, galon olarak" #: Form/form_us.xml.h:533 msgid "Pounds Butter made on farm in 1879" -msgstr "" +msgstr "1879 yılında çiftlikte üretilen tereyağı, pound olarak" #: Form/form_us.xml.h:534 msgid "Pounds Cheese made on farm in 1879" -msgstr "" +msgstr "1879 yılında çiftlikte üretilen peynir, pound olarak" #: Form/form_us.xml.h:535 msgid "Sheep on hand" -msgstr "" +msgstr "Eldeki koyun sayısı" #: Form/form_us.xml.h:536 msgid "Lambs Dropped in 1879" -msgstr "" +msgstr "1879 yılında doğan kuzu sayısı" #: Form/form_us.xml.h:537 msgid "Sheep and Lambs Purchased in 1879" -msgstr "" +msgstr "1879 yılında satın alınan koyun ve kuzu sayısı" #: Form/form_us.xml.h:538 msgid "Sheep and Lambs Sold living in 1879" -msgstr "" +msgstr "1879 yılında canlı olarak satılan koyun ve kuzu sayısı" #: Form/form_us.xml.h:539 msgid "Sheep and Lambs Slaughtered in 1879" -msgstr "" +msgstr "1879 yılında kesilen koyun ve kuzu sayısı" #: Form/form_us.xml.h:540 msgid "Sheep and Lambs Killed by dogs in 1879" -msgstr "" +msgstr "1879 yılında köpekler tarafından öldürülen koyun ve kuzu sayısı" #: Form/form_us.xml.h:541 msgid "Sheep and Lambs Died of Disease in 1879" -msgstr "" +msgstr "1879 yılında hastalıktan ölen koyun ve kuzu sayısı" #: Form/form_us.xml.h:542 msgid "Sheep and Lambs Died of stress or weather in 1879" msgstr "" +"1879 yılında stres veya hava koşulları nedeniyle ölen koyun ve kuzu sayısı" #: Form/form_us.xml.h:543 msgid "Number of Fleece, Spring 1880" -msgstr "" +msgstr "1880 İlkbaharında kırkılan yapağı sayısı" #: Form/form_us.xml.h:544 msgid "Pounds Wool, Spring 1880" -msgstr "" +msgstr "1880 İlkbaharında elde edilen yün, pound olarak" #: Form/form_us.xml.h:545 msgid "Swine on hand" -msgstr "" +msgstr "Eldeki domuz sayısı" #: Form/form_us.xml.h:546 Form/form_us.xml.h:549 msgid "Barnyard Poultry on hand" -msgstr "" +msgstr "Eldeki kümes hayvanı sayısı" #: Form/form_us.xml.h:547 msgid "Other Poultry on hand" -msgstr "" +msgstr "Eldeki diğer kanatlı hayvan sayısı" #: Form/form_us.xml.h:548 msgid "Dozen eggs produced in 1879" -msgstr "" +msgstr "1879 yılında üretilen yumurta, düzine olarak" #: Form/form_us.xml.h:550 msgid "Acres Barley in 1879" -msgstr "" +msgstr "1879 yılında arpa ekili alan, dönüm olarak" #: Form/form_us.xml.h:551 msgid "Bushels Barley in 1879" -msgstr "" +msgstr "1879 yılında üretilen arpa, kile olarak" #: Form/form_us.xml.h:552 msgid "Acres Buckwheat in 1879" -msgstr "" +msgstr "1879 yılında karabuğday ekili alan, dönüm olarak" #: Form/form_us.xml.h:553 msgid "Bushels Buckwheat in 1879" -msgstr "" +msgstr "1879 yılında üretilen karabuğday, kile olarak" #: Form/form_us.xml.h:554 msgid "Acres Indian Corn in 1879" -msgstr "" +msgstr "1879 yılında mısır ekili alan, dönüm olarak" #: Form/form_us.xml.h:555 msgid "Bushels Indian Corn in 1879" -msgstr "" +msgstr "1879 yılında üretilen mısır, kile olarak" #: Form/form_us.xml.h:556 msgid "Acres Oats in 1879" -msgstr "" +msgstr "1879 yılında yulaf ekili alan, dönüm olarak" #: Form/form_us.xml.h:557 msgid "Bushels Oats in 1879" -msgstr "" +msgstr "1879 yılında üretilen yulaf, kile olarak" #: Form/form_us.xml.h:558 msgid "Acres Rye in 1879" -msgstr "" +msgstr "1879 yılında çavdar ekili alan, dönüm olarak" #: Form/form_us.xml.h:559 msgid "Bushels Rye in 1879" -msgstr "" +msgstr "1879 yılında üretilen çavdar, kile olarak" #: Form/form_us.xml.h:560 msgid "Acres Wheat in 1879" -msgstr "" +msgstr "1879 yılında buğday ekili alan, dönüm olarak" #: Form/form_us.xml.h:561 msgid "Bushels Wheat in 1879" -msgstr "" +msgstr "1879 yılında üretilen buğday, kile olarak" #: Form/form_us.xml.h:562 msgid "Bushels Canadian Peas in 1879" -msgstr "" +msgstr "1879 yılında üretilen Kanada bezelyesi, kile olarak" #: Form/form_us.xml.h:563 msgid "Bushels Beans in 1879" -msgstr "" +msgstr "1879 yılında üretilen fasulye, kile olarak" #: Form/form_us.xml.h:564 msgid "Acres Flax in 1879" -msgstr "" +msgstr "1879 yılında keten ekili alan, dönüm olarak" #: Form/form_us.xml.h:565 msgid "Bushels Flax Sued in 1879" -msgstr "" +msgstr "1879'da Ekilen Keten,kile olarak" #: Form/form_us.xml.h:566 msgid "Tons Flax Straw in 1879" -msgstr "" +msgstr "1879'da Ekilen Keten Samanı, ton olarak" #: Form/form_us.xml.h:567 msgid "Pounds Flax Fiber in 1879" -msgstr "" +msgstr "1879'da Ekilen Keten Lifi, pound olarak" #: Form/form_us.xml.h:568 msgid "Acres Hemp in 1879" -msgstr "" +msgstr "1879'da Ekilen Kenevir, dönüm olarak" #: Form/form_us.xml.h:569 msgid "Tons Hemp in 1879" -msgstr "" +msgstr "1879'da Ekilen Kenevir, ton olarak" #: Form/form_us.xml.h:570 msgid "Acres Sorghum in 1879" -msgstr "" +msgstr "1879'da Ekilen Sorgum Şekeri, dönüm olarak" #: Form/form_us.xml.h:571 msgid "Pounds Sorghum Sugar in 1879" -msgstr "" +msgstr "1879'da Ekilen Sorgum Şekeri, pound olarak" #: Form/form_us.xml.h:572 msgid "Gallons Sorghum Molasses in 1879" -msgstr "" +msgstr "1879'da Ekilen Sorgum Pekmezi, galon olarak" #: Form/form_us.xml.h:573 msgid "Pounds Maple Sugar in 1879" -msgstr "" +msgstr "1879'da Ekilen Akçaağaç Şekeri, pound olarak" #: Form/form_us.xml.h:574 msgid "Gallons Maple Molasses in 1879" -msgstr "" +msgstr "1879'da Ekilen Akçaağaç Pekmezi, galon olarak" #: Form/form_us.xml.h:575 msgid "Acres Broom Corn in 1879" -msgstr "" +msgstr "1879'da Ekilen Süpürge Mısırı, dönüm olarak" #: Form/form_us.xml.h:576 msgid "Pounds Broom Corn in 1879" -msgstr "" +msgstr "1879'da Ekilen Süpürge Mısırı, pound olarak" #: Form/form_us.xml.h:577 msgid "Acres Hops in 1879" -msgstr "" +msgstr "1879'da Ekilen Şerbetçi Otu, dönüm olarak" #: Form/form_us.xml.h:578 msgid "Pounds Hops in 1879" -msgstr "" +msgstr "1879'da Ekilen Şerbetçi Otu, pound olarak" #: Form/form_us.xml.h:579 msgid "Acres Potatoes (Irish) in 1879" -msgstr "" +msgstr "1879'da Ekilen Patates (İrlanda), dönüm olarak" #: Form/form_us.xml.h:580 msgid "Bushels Potatoes (Irish) in 1879" -msgstr "" +msgstr "1879'da Ekilen Patates (İrlanda), kile olarak" #: Form/form_us.xml.h:581 msgid "Acres Potatoes (Sweet) in 1879" -msgstr "" +msgstr "1879'da Ekilen Patates (Tatlı), dönüm olarak" #: Form/form_us.xml.h:582 msgid "Bushels Potatoes (Sweet) in 1879" -msgstr "" +msgstr "1879'da Patates (Tatlı) Miktarı, kile olarak" #: Form/form_us.xml.h:583 msgid "Acres Tobacco in 1879" -msgstr "" +msgstr "1879'da Tütün Alanı, dönüm olarak" #: Form/form_us.xml.h:584 msgid "Pounds Tobacco in 1879" -msgstr "" +msgstr "1879'da Tütün Miktarı, pound olarak" #: Form/form_us.xml.h:585 msgid "Acres Apple Orchards in 1879" -msgstr "" +msgstr "1879'da Elma Bahçesi Alanı, dönüm olarak" #: Form/form_us.xml.h:586 msgid "Number of Bearing Apple Trees in 1879" -msgstr "" +msgstr "1879'da Meyve Veren Elma Ağacı Sayısı" #: Form/form_us.xml.h:587 msgid "Bushels Apples in 1879" -msgstr "" +msgstr "1879'da Elma Miktarı, kile olarak" #: Form/form_us.xml.h:588 msgid "Acres Peach Orchards in 1879" -msgstr "" +msgstr "1879'da Şeftali Bahçesi Alanı, dönüm olarak" #: Form/form_us.xml.h:589 msgid "Number of Bearing Peach Trees in 1879" -msgstr "" +msgstr "1879'da Meyve Veren Şeftali Ağacı Sayısı" #: Form/form_us.xml.h:590 msgid "Bushels Peaches in 1879" -msgstr "" +msgstr "1879'da Şeftali Miktarı, kile olarak" #: Form/form_us.xml.h:591 msgid "Total value of Orchard Products Sold or Consumed in 1879" -msgstr "" +msgstr "1879'da Satılan veya Tüketilen Bahçe Ürünlerinin Toplam Değeri" #: Form/form_us.xml.h:592 msgid "Acres Nurseries" -msgstr "" +msgstr "Fidanlık Alanı, dönüm olarak" #: Form/form_us.xml.h:593 msgid "Value of Nursery Products Sold" -msgstr "" +msgstr "Satılan Fidanlık Ürünlerinin Değeri" #: Form/form_us.xml.h:594 msgid "Acres Vineyards" -msgstr "" +msgstr "Bağ Alanı, dönüm olarak" #: Form/form_us.xml.h:595 msgid "Grapes sold in 1879" -msgstr "" +msgstr "1879'da Satılan Üzüm" #: Form/form_us.xml.h:596 msgid "Wine made in 1879" -msgstr "" +msgstr "1879'da Üretilen Şarap" #: Form/form_us.xml.h:597 msgid "Value of Market Garden Produce Sold in 1879" -msgstr "" +msgstr "1879'da Satılan Pazar Bahçesi Ürünlerinin Değeri" #: Form/form_us.xml.h:598 msgid "Pounds Honey in 1879" -msgstr "" +msgstr "1879'da Bal Miktarı, pound olarak" #: Form/form_us.xml.h:599 msgid "Pounds Beeswax in 1879" -msgstr "" +msgstr "1879'da Balmumu Miktarı, pound olarak" #: Form/form_us.xml.h:600 msgid "Cords of Wood cut in 1879" -msgstr "" +msgstr "1879'da Kesilen Odun Miktarı, kordon olarak" #: Form/form_us.xml.h:601 msgid "Value of forest products sold or consumed in 1879" -msgstr "" +msgstr "1879'da Satılan veya Tüketilen Orman Ürünlerinin Değeri" #: Form/form_us.xml.h:611 msgid "Street and No." -msgstr "" +msgstr "Sokak ve No." #: Form/form_us.xml.h:612 Form/form_us.xml.h:1244 Form/form_us.xml.h:1272 #: Form/form_us.xml.h:1313 Form/form_us.xml.h:1382 Form/form_us.xml.h:1673 @@ -11754,957 +11792,977 @@ msgstr "" #: Form/form_us.xml.h:2118 Form/form_us.xml.h:2156 Form/form_us.xml.h:2194 #: Form/form_us.xml.h:2297 msgid "Ward" -msgstr "" +msgstr "Mahalle" #: Form/form_us.xml.h:617 msgid "Veteran of Civil War or widow of a veteran" -msgstr "" +msgstr "İç Savaş gazisi veya bir gazinin dul eşi" #: Form/form_us.xml.h:622 Form/form_us.xml.h:679 Form/form_us.xml.h:718 #: Form/form_us.xml.h:763 Form/form_us.xml.h:1076 Form/form_us.xml.h:1610 msgid "Single, married, widowed or divorced" -msgstr "" +msgstr "Bekâr, evli, dul veya boşanmış" #: Form/form_us.xml.h:624 msgid "Mother of how many children and number of children living" -msgstr "" +msgstr "Kaç çocuğun annesi olduğu ve yaşayan çocuk sayısı" #: Form/form_us.xml.h:628 Form/form_us.xml.h:687 msgid "Number of years in US" -msgstr "" +msgstr "ABD'de geçirilen yıl sayısı" #: Form/form_us.xml.h:629 Form/form_us.xml.h:1438 msgid "Naturalized" -msgstr "" +msgstr "Vatandaşlığa alınmış" #: Form/form_us.xml.h:630 Form/form_us.xml.h:689 msgid "Naturalization papers taken out" -msgstr "" +msgstr "Vatandaşlık belgeleri için başvurulmuş" #: Form/form_us.xml.h:633 Form/form_us.xml.h:733 Form/form_us.xml.h:768 #: Form/form_us.xml.h:1619 msgid "Able to read" -msgstr "" +msgstr "Okuyabilir" #: Form/form_us.xml.h:634 Form/form_us.xml.h:734 Form/form_us.xml.h:769 #: Form/form_us.xml.h:1620 msgid "Able to write" -msgstr "" +msgstr "Yazabilir" #: Form/form_us.xml.h:635 msgid "Able to speak English; if not, the language spoken" -msgstr "" +msgstr "İngilizce konuşabilir; değilse, konuşulan dil" #: Form/form_us.xml.h:636 msgid "Suffering from disease" -msgstr "" +msgstr "Hastalıktan muzdarip" #: Form/form_us.xml.h:637 msgid "Whether defective and name of defect" -msgstr "" +msgstr "Kusurlu olup olmadığı ve kusurun adı" #: Form/form_us.xml.h:638 msgid "Prisoner, convict, homeless child or pauper" -msgstr "" +msgstr "Mahkûm, hükümlü, evsiz çocuk veya yoksul" #: Form/form_us.xml.h:639 msgid "Supplemental schedule and page" -msgstr "" +msgstr "Ek zamanlama ve sayfa" #: Form/form_us.xml.h:643 msgid "Supervisor's District No." -msgstr "" +msgstr "Denetçi Bölge No." #: Form/form_us.xml.h:644 Form/form_us.xml.h:1068 Form/form_us.xml.h:1668 msgid "Enumeration District No." -msgstr "" +msgstr "Sayım Bölgesi No." #: Form/form_us.xml.h:649 Form/form_us.xml.h:1013 Form/form_us.xml.h:2291 #: Form/form_us.xml.h:2320 RelID/relation_tab.py:345 RelID/relation_tab.py:601 msgid "Rank" -msgstr "" +msgstr "Rütbe" #: Form/form_us.xml.h:650 Form/form_us.xml.h:1489 Form/form_us.xml.h:1534 #: Form/form_us.xml.h:2093 Form/form_us.xml.h:2129 Form/form_us.xml.h:2167 #: Form/form_us.xml.h:2205 Form/form_us.xml.h:2609 msgid "Company" -msgstr "" +msgstr "Bölük" #: Form/form_us.xml.h:651 msgid "Regiment/Vessel" -msgstr "" +msgstr "Alay/Gemi" #: Form/form_us.xml.h:652 Form/form_us.xml.h:1011 msgid "Date Enlisted" -msgstr "" +msgstr "Askere Alınma Tarihi" #: Form/form_us.xml.h:653 msgid "Date Discharged" -msgstr "" +msgstr "Terhis Tarihi" #: Form/form_us.xml.h:654 msgid "Length of Service" -msgstr "" +msgstr "Hizmet Süresi" #: Form/form_us.xml.h:656 msgid "Disability Incurred" -msgstr "" +msgstr "Meydana Gelen Sakatlık" #: Form/form_us.xml.h:660 Form/form_us.xml.h:702 Form/form_us.xml.h:745 msgid "State (or Territory) of" -msgstr "" +msgstr "Eyalet (veya Bölge)" #: Form/form_us.xml.h:666 Form/form_us.xml.h:708 Form/form_us.xml.h:750 #: Form/form_us.xml.h:785 Form/form_us.xml.h:828 msgid "Division of County" -msgstr "" +msgstr "İlçe Bölümü" #: Form/form_us.xml.h:667 Form/form_us.xml.h:709 Form/form_us.xml.h:751 #: Form/form_us.xml.h:2002 msgid "Name of Institution" -msgstr "" +msgstr "Kurumun Adı" #: Form/form_us.xml.h:668 Form/form_us.xml.h:710 Form/form_us.xml.h:752 msgid "City, Town, Village" -msgstr "" +msgstr "Şehir, Kasaba, Köy" #: Form/form_us.xml.h:669 Form/form_us.xml.h:711 Form/form_us.xml.h:753 #: Form/form_us.xml.h:787 Form/form_us.xml.h:830 msgid "Ward of city" -msgstr "" +msgstr "Şehrin bölgesi" #: Form/form_us.xml.h:670 Form/form_us.xml.h:755 Form/form_us.xml.h:795 #: Form/form_us.xml.h:838 Form/form_us.xml.h:1069 Form/form_us.xml.h:2616 msgid "Street Address" -msgstr "" +msgstr "Sokak Adresi" #: Form/form_us.xml.h:673 Form/form_us.xml.h:1604 msgid "Relation to head" -msgstr "" +msgstr "Aile reisiyle ilişkisi" #: Form/form_us.xml.h:676 msgid "Month of birth" -msgstr "" +msgstr "Doğum ayı" #: Form/form_us.xml.h:680 Form/form_us.xml.h:719 msgid "Years of present marriage" -msgstr "" +msgstr "Mevcut evliliğin yılı" #: Form/form_us.xml.h:681 Form/form_us.xml.h:720 msgid "Mother of how many children" -msgstr "" +msgstr "Annenin sahip olduğu çocuk sayısı" #: Form/form_us.xml.h:682 Form/form_us.xml.h:721 msgid "Number of children living" -msgstr "" +msgstr "Yaşayan çocuk sayısı" #: Form/form_us.xml.h:686 Form/form_us.xml.h:725 Form/form_us.xml.h:764 #: Form/form_us.xml.h:813 msgid "Year of immigration" -msgstr "" +msgstr "Göç yılı" #: Form/form_us.xml.h:691 msgid "Months not employed" -msgstr "" +msgstr "Çalışmadığı ay sayısı" #: Form/form_us.xml.h:692 Form/form_us.xml.h:1086 msgid "Attended school" -msgstr "" +msgstr "Okula devam etti" #: Form/form_us.xml.h:693 Form/form_us.xml.h:1320 msgid "Can read" -msgstr "" +msgstr "Okuyabilir" #: Form/form_us.xml.h:694 Form/form_us.xml.h:1322 msgid "Can write" -msgstr "" +msgstr "Yazabilir" #: Form/form_us.xml.h:695 msgid "Can speak English" -msgstr "" +msgstr "İngilizce konuşabilir" #: Form/form_us.xml.h:696 Form/form_us.xml.h:736 Form/form_us.xml.h:758 #: Form/form_us.xml.h:798 Form/form_us.xml.h:1557 Form/form_us.xml.h:1579 #: Form/form_us.xml.h:1605 msgid "Home owned or rented" -msgstr "" +msgstr "Ev sahibi veya kiracı" #: Form/form_us.xml.h:697 Form/form_us.xml.h:737 msgid "Owned free or mortgaged" -msgstr "" +msgstr "Tamamen kendisine ait veya ipotekli" #: Form/form_us.xml.h:698 Form/form_us.xml.h:738 Form/form_us.xml.h:1559 #: Form/form_us.xml.h:1581 msgid "Farm or house" -msgstr "" +msgstr "Çiftlik veya ev" #: Form/form_us.xml.h:699 Form/form_us.xml.h:739 Form/form_us.xml.h:780 #: Form/form_us.xml.h:823 msgid "Number of farm schedule" -msgstr "" +msgstr "Çiftlik zamanlama numarası" #: Form/form_us.xml.h:726 Form/form_us.xml.h:765 Form/form_us.xml.h:814 msgid "Naturalized or alien" -msgstr "" +msgstr "Vatandaşlığa alınmış veya yabancı" #: Form/form_us.xml.h:727 msgid "Able to speak English; if not, language spoken" -msgstr "" +msgstr "İngilizce konuşabilir; değilse, konuştuğu dil" #: Form/form_us.xml.h:729 msgid "Nature of industry" -msgstr "" +msgstr "Endüstrinin niteliği" #: Form/form_us.xml.h:730 msgid "Employer, employee or working on own account" -msgstr "" +msgstr "İşveren, çalışan veya kendi hesabına çalışan" #: Form/form_us.xml.h:731 msgid "Out of work" -msgstr "" +msgstr "İşsiz" #: Form/form_us.xml.h:732 msgid "Weeks out of work in 1909" -msgstr "" +msgstr "1909 yılında işsiz kaldığı haftalar" #: Form/form_us.xml.h:735 msgid "Attended school since Sept. 1, 1909" -msgstr "" +msgstr "1 Eylül 1909'dan sonra okula devam etti" #: Form/form_us.xml.h:740 msgid "Civil War veteran" -msgstr "" +msgstr "İç Savaş gazisi" #: Form/form_us.xml.h:759 msgid "If owned, free or mortgaged" -msgstr "" +msgstr "Mülkiyeti varsa, tamamen kendisine ait veya ipotekli" #: Form/form_us.xml.h:766 Form/form_us.xml.h:1615 msgid "Year of naturalization" -msgstr "" +msgstr "Vatandaşlığa alınma yılı" #: Form/form_us.xml.h:767 msgid "Attended school since Sept. 1, 1919" -msgstr "" +msgstr "1 Eylül 1919'dan sonra okula devam etti" #: Form/form_us.xml.h:771 Form/form_us.xml.h:872 msgid "Mother tongue" -msgstr "" +msgstr "Ana dili" #: Form/form_us.xml.h:773 msgid "Father mother tongue" -msgstr "" +msgstr "Babanın ana dili" #: Form/form_us.xml.h:775 msgid "Mother's mother tongue" -msgstr "" +msgstr "Annenin ana dili" #: Form/form_us.xml.h:776 Form/form_us.xml.h:815 msgid "Able to speak English" -msgstr "" +msgstr "İngilizce konuşabilir" #: Form/form_us.xml.h:779 msgid "Employer, worker or working on own account" -msgstr "" +msgstr "İşveren, işçi veya kendi hesabına çalışan" #: Form/form_us.xml.h:786 Form/form_us.xml.h:829 msgid "Incorporated place" -msgstr "" +msgstr "Birleştirilmiş yerleşim yeri" #: Form/form_us.xml.h:788 Form/form_us.xml.h:831 msgid "Unincorporated place" -msgstr "" +msgstr "Birleştirilmemiş yerleşim yeri" #: Form/form_us.xml.h:799 msgid "Value of home or monthly rental" -msgstr "" +msgstr "Evin değeri veya aylık kira bedeli" #: Form/form_us.xml.h:800 msgid "Radio set" -msgstr "" +msgstr "Radyo cihazı" #: Form/form_us.xml.h:801 Form/form_us.xml.h:842 Form/form_us.xml.h:898 msgid "Farm?" -msgstr "" +msgstr "Çiftlik mi?" #: Form/form_us.xml.h:805 msgid "Marital condition" -msgstr "" +msgstr "Medeni durum" #: Form/form_us.xml.h:806 Form/form_us.xml.h:883 msgid "Age at first marriage" -msgstr "" +msgstr "İlk evlilik yaşı" #: Form/form_us.xml.h:807 msgid "Attended school since Sept. 1, 1929" -msgstr "" +msgstr "1 Eylül 1929'dan beri okula devam etti" #: Form/form_us.xml.h:808 msgid "Able to read and write" -msgstr "" +msgstr "Okuyup yazabiliyor" #: Form/form_us.xml.h:812 msgid "Language before coming to US" -msgstr "" +msgstr "ABD'ye gelmeden önce konuştuğu dil" #: Form/form_us.xml.h:818 Form/form_us.xml.h:865 Form/form_us.xml.h:927 msgid "Class of worker" -msgstr "" +msgstr "Çalışan sınıfı" #: Form/form_us.xml.h:820 msgid "Line number for unemployed" -msgstr "" +msgstr "İşsizler için sıra numarası" #: Form/form_us.xml.h:821 msgid "Veteran" -msgstr "" +msgstr "Gazi" #: Form/form_us.xml.h:822 msgid "What war" -msgstr "" +msgstr "Hangi savaş" #: Form/form_us.xml.h:840 msgid "Home Owned/Rented" -msgstr "" +msgstr "Ev Sahibi/Kiracı" #: Form/form_us.xml.h:841 msgid "Home value/monthly rental" -msgstr "" +msgstr "Ev değeri/aylık kira" #: Form/form_us.xml.h:845 msgid "Color/Race" -msgstr "" +msgstr "Renk/Irk" #: Form/form_us.xml.h:846 msgid "Age at Last Birthday" -msgstr "" +msgstr "Son doğum günündeki yaşı" #: Form/form_us.xml.h:847 Form/form_us.xml.h:1257 Form/form_us.xml.h:1346 #: Form/form_us.xml.h:1409 Form/form_us.xml.h:1431 Form/form_us.xml.h:1494 #: Form/form_us.xml.h:2335 Form/form_us.xml.h:2438 Form/form_us.xml.h:2471 #: Form/form_us.xml.h:2492 msgid "Marital Status" -msgstr "" +msgstr "Medeni Durum" #: Form/form_us.xml.h:848 msgid "Attended school/college since March 1, 1940" -msgstr "" +msgstr "1 Mart 1940'tan beri okula/üniversiteye devam etti" #: Form/form_us.xml.h:849 msgid "Highest grade completed" -msgstr "" +msgstr "Tamamlanan en yüksek sınıf" #: Form/form_us.xml.h:851 msgid "Citizenship if foreign born" -msgstr "" +msgstr "Yabancı doğumluysa vatandaşlık durumu" #: Form/form_us.xml.h:852 msgid "City (April 1, 1935)" -msgstr "" +msgstr "Şehir (1 Nisan 1935)" #: Form/form_us.xml.h:854 msgid "State (Territory/Country)" -msgstr "" +msgstr "Eyalet (Bölge/Ülke)" #: Form/form_us.xml.h:855 msgid "On a Farm (Y/N)" -msgstr "" +msgstr "Çiftlikte mi? (E/H)" #: Form/form_us.xml.h:856 msgid "At Work during week March 24-30" -msgstr "" +msgstr "24-30 Mart haftasında çalışıyor muydu" #: Form/form_us.xml.h:857 msgid "public Emergency Work?" -msgstr "" +msgstr "Kamu acil durum çalışması mı?" #: Form/form_us.xml.h:858 msgid "Seeking work?" -msgstr "" +msgstr "İş mi arıyor?" #: Form/form_us.xml.h:859 msgid "Have a Job?" -msgstr "" +msgstr "Bir işi var mı?" #: Form/form_us.xml.h:860 msgid "housework (H), school (S), unable to work (U) or other (Ot)" -msgstr "" +msgstr "Ev işleri (E), okul (O), çalışamaz durumda (D veya diğer (Di)" #: Form/form_us.xml.h:861 msgid "# hours worked during week March 24-30, 1940" -msgstr "" +msgstr "# 24-30 Mart 1940 haftasında çalışılan saat sayısı" #: Form/form_us.xml.h:862 msgid "Weeks unemployed up to March 30, 1940" -msgstr "" +msgstr "30 Mart 1940'a kadar işsiz kalınan hafta sayısı" #: Form/form_us.xml.h:866 msgid "# weeks worked in 1939" -msgstr "" +msgstr "# 1939'da çalışılan hafta sayısı" #: Form/form_us.xml.h:867 msgid "Amount of money, wages, salary in 1939" -msgstr "" +msgstr "1939'daki para , ücret, maaş miktarı" #: Form/form_us.xml.h:868 msgid "Income of more than $50 from other sources?" -msgstr "" +msgstr "Diğer kaynaklardan 50 dolardan fazla gelir var mı?" #: Form/form_us.xml.h:869 msgid "Number of Farm Schedule" -msgstr "" +msgstr "Çiftlik Zamanlama Numarası" #: Form/form_us.xml.h:873 msgid "Veteran?" -msgstr "" +msgstr "Gazi mi?" #: Form/form_us.xml.h:874 msgid "If child, veteran-father dead?" -msgstr "" +msgstr "Çocuksa, gazi babası ölü mü?" #: Form/form_us.xml.h:875 msgid "War/Military Service" -msgstr "" +msgstr "Savaş/Askerlik Hizmeti" #: Form/form_us.xml.h:876 msgid "Have Federal SSN?" -msgstr "" +msgstr "Federal Sosyal Güvenlik Numarası var mı?" #: Form/form_us.xml.h:877 msgid "Deductions made in 1939?" -msgstr "" +msgstr "1939'da kesinti yapıldı mı?" #: Form/form_us.xml.h:878 msgid "If so, made from all, 1/2 or more, less from wages in 1939?" msgstr "" +"Eğer yapıldıysa, 1939 ücretlerinden tamamından, yarısından veya daha " +"fazlasından, daha azından mı kesildi?" #: Form/form_us.xml.h:879 msgid "Usual Occupation" -msgstr "" +msgstr "Yaptığı olağan meslek" #: Form/form_us.xml.h:880 msgid "Usual Industry" -msgstr "" +msgstr "Yaptığı olağan sektör" #: Form/form_us.xml.h:881 msgid "Usual Class of Work" -msgstr "" +msgstr "Yaptığı olağan iş sınıfı" #: Form/form_us.xml.h:882 msgid "If woman, married more than once?" -msgstr "" +msgstr "Kadınsa, birden fazla kez evlenmiş mi?" #: Form/form_us.xml.h:884 msgid "Number of children ever born" -msgstr "" +msgstr "Doğan çocuk sayısı" #: Form/form_us.xml.h:889 msgid "Incorporated place or township" -msgstr "" +msgstr "Birleşik yer veya kasaba" #: Form/form_us.xml.h:890 msgid "E. D. Number" -msgstr "" +msgstr "E. D. Numarası" #: Form/form_us.xml.h:892 msgid "Hotel, large rooming house, institution, military installation, etc." -msgstr "" +msgstr "Otel, büyük pansiyon, kurum, askeri tesis vb." #: Form/form_us.xml.h:899 msgid "Is this house on a farm (or ranch)?" -msgstr "" +msgstr "Bu ev bir çiftlikte (veya çiftlik arazisinde) mi?" #: Form/form_us.xml.h:900 msgid "On 3+ acres of land?" -msgstr "" +msgstr "3+ dönüm arazi üzerinde mi?" #: Form/form_us.xml.h:901 msgid "Is this house on a place of three or more acres?" -msgstr "" +msgstr "Bu ev üç veya daha fazla dönümlük bir arazi üzerinde mi?" #: Form/form_us.xml.h:902 msgid "Agricultural Questionnaire Number" -msgstr "" +msgstr "Tarım Anketi Numarası" #: Form/form_us.xml.h:906 msgid "Age at last birthday" -msgstr "" +msgstr "Son doğum günündeki yaş" #: Form/form_us.xml.h:907 msgid "How old was he on his last birthday?" -msgstr "" +msgstr "Son doğum gününde kaç yaşındaydı?" #: Form/form_us.xml.h:908 Form/form_us.xml.h:2537 msgid "Marital status" -msgstr "" +msgstr "Medeni durumu" #: Form/form_us.xml.h:909 msgid "Is he now married, widowed, divorced, separated, or never married?" -msgstr "" +msgstr "Şu anda evli, dul, boşanmış, ayrı mı yaşıyor yoksa hiç evlenmemiş mi?" #: Form/form_us.xml.h:911 msgid "What State (or foreign country) was he born in?" -msgstr "" +msgstr "Hangi eyalette (veya yabancı ülkede) doğdu?" #: Form/form_us.xml.h:912 msgid "Is he naturalized?" -msgstr "" +msgstr "Vatandaşlığa kabul edildi mi?" #: Form/form_us.xml.h:913 msgid "housework (H), work (W), unable to work (U) or other (Ot)" -msgstr "" +msgstr "Ev işleri (E), iş (İ), çalışamayacak durumda (Ç) veya diğer (Di)" #: Form/form_us.xml.h:914 msgid "" "What was this person doing most of last week—working, keeping house, or " "something else?" msgstr "" +"Bu kişi geçen haftanın büyük bölümünde ne yapıyordu—çalışıyor muydu, ev " +"işleriyle mi uğraşıyordu, yoksa başka bir şey mi yapıyordu?" #: Form/form_us.xml.h:915 msgid "Worked last week? (Y/N)" -msgstr "" +msgstr "Geçen hafta çalıştı mı? (E/H)" #: Form/form_us.xml.h:916 msgid "" "Did this person do any work at all last week, not counting work around the " "house?" -msgstr "" +msgstr "Bu kişi geçen hafta ev işleri dışında herhangi bir iş yaptı mı?" #: Form/form_us.xml.h:917 msgid "Looking for work?" -msgstr "" +msgstr "İş arıyor mu?" #: Form/form_us.xml.h:918 msgid "Was this person looking for work?" -msgstr "" +msgstr "Bu kişi iş arıyor muydu?" #: Form/form_us.xml.h:919 msgid "Has a job or business?" -msgstr "" +msgstr "Bir işi veya işletmesi var mı?" #: Form/form_us.xml.h:920 msgid "Even though he didn’t work last week, does he have a job or business?" -msgstr "" +msgstr "Geçen hafta çalışmamış olsa bile, bir işi veya işletmesi var mı?" #: Form/form_us.xml.h:921 msgid "# hours worked last week" -msgstr "" +msgstr "# Geçen hafta çalıştığı saat sayısı" #: Form/form_us.xml.h:922 msgid "How many hours did he work last week?" -msgstr "" +msgstr "Geçen hafta kaç saat çalıştı?" #: Form/form_us.xml.h:923 msgid "Type of work" -msgstr "" +msgstr "İş türü" #: Form/form_us.xml.h:924 msgid "What kind of work was he doing?" -msgstr "" +msgstr "Ne tür bir iş yapıyordu?" #: Form/form_us.xml.h:925 msgid "Business or industry" -msgstr "" +msgstr "İşletme veya sektör" #: Form/form_us.xml.h:926 msgid "What kind of business or industry was he working in?" -msgstr "" +msgstr "Hangi tür işletme veya sektörde çalışıyordu?" #: Form/form_us.xml.h:928 msgid "Same house in Apr 1949?" -msgstr "" +msgstr "Nisan 1949'da aynı evde miydi?" #: Form/form_us.xml.h:929 msgid "Was he living in this same house a year ago?" -msgstr "" +msgstr "Bir yıl önce de aynı evde mi yaşıyordu?" #: Form/form_us.xml.h:930 msgid "Farm in Apr 1949?" -msgstr "" +msgstr "Nisan 1949'da çiftlikte miydi?" #: Form/form_us.xml.h:931 msgid "Was he living on a farm a year ago?" -msgstr "" +msgstr "Bir yıl önce bir çiftlikte mi yaşıyordu?" #: Form/form_us.xml.h:932 msgid "Same county in Apr 1949?" -msgstr "" +msgstr "Nisan 1949'da aynı ilçede miydi?" #: Form/form_us.xml.h:933 msgid "Was he living in this same county a year ago?" -msgstr "" +msgstr "Bir yıl önce de aynı ilçede mi yaşıyordu?" #: Form/form_us.xml.h:935 msgid "State or foreign country" -msgstr "" +msgstr "Eyalet veya yabancı ülke" #: Form/form_us.xml.h:937 msgid "What country was his father born in?" -msgstr "" +msgstr "Babası hangi ülkede doğdu?" #: Form/form_us.xml.h:939 msgid "What country was his mother born in?" -msgstr "" +msgstr "Annesi hangi ülkede doğdu?" #: Form/form_us.xml.h:940 msgid "Highest grade attended" -msgstr "" +msgstr "Devam ettiği en yüksek sınıf" #: Form/form_us.xml.h:941 msgid "What is the highest grade of school that he has attended?" -msgstr "" +msgstr "Devam ettiği en yüksek okul sınıfı hangisidir?" #: Form/form_us.xml.h:942 msgid "Completed?" -msgstr "" +msgstr "Tamamladı mı?" #: Form/form_us.xml.h:943 msgid "Did he finish this grade?" -msgstr "" +msgstr "Bu sınıfı bitirdi mi?" #: Form/form_us.xml.h:944 msgid "Attended school since Feb 1, 1950?" -msgstr "" +msgstr "1 Şubat 1950'den beri okula devam etti mi?" #: Form/form_us.xml.h:945 msgid "Has he attended school at any time since February 1st?" -msgstr "" +msgstr "1 Şubat'tan bu yana herhangi bir zamanda okula devam etti mi?" #: Form/form_us.xml.h:946 msgid "How many weeks has he been looking for work?" -msgstr "" +msgstr "Kaç haftadır iş arıyor?" #: Form/form_us.xml.h:947 msgid "# weeks worked in 1949" -msgstr "" +msgstr "# 1949'da çalıştığı hafta sayısı" #: Form/form_us.xml.h:948 msgid "" "Last year, in how many weeks did this person do any work at all, not " "counting work around the house?" msgstr "" +"Geçen yıl, ev işleri hariç olmak üzere bu kişi toplam kaç hafta herhangi bir " +"işte çalıştı?" #: Form/form_us.xml.h:949 msgid "Amount of wages/salary in 1949" -msgstr "" +msgstr "1949'daki ücret/maaş tutarı" #: Form/form_us.xml.h:950 msgid "" "Last year, how much money did he earn working as an employee for wages or " "salary?" msgstr "" +"Geçen yıl, ücretli veya maaşlı bir çalışan olarak ne kadar para kazandı?" #: Form/form_us.xml.h:951 msgid "Amount of money from own business in 1949" -msgstr "" +msgstr "1949'da kendi işinden elde ettiği gelir tutarı" #: Form/form_us.xml.h:952 msgid "" "Last year, how much money did he earn working in his own business, " "professional practice, or farm?" msgstr "" +"Geçen yıl, kendi işi, serbest mesleği veya çiftliğinde çalışarak ne kadar " +"para kazandı?" #: Form/form_us.xml.h:953 msgid "Amount of other income in 1949" -msgstr "" +msgstr "1949'daki diğer gelir tutarı" #: Form/form_us.xml.h:954 msgid "" "Last year, how much money did he receive from interest, dividends, veteran’s " "allowances, pensions, rents, or other income?" msgstr "" +"Geçen yıl, faiz, temettü, gazi ödenekleri, emekli maaşları, kira gelirleri " +"veya diğer gelirlerden ne kadar para aldı?" #: Form/form_us.xml.h:955 msgid "Relatives in household wages/salary in 1949" -msgstr "" +msgstr "1949'da hanedeki akrabaların ücret/maaş geliri" #: Form/form_us.xml.h:956 msgid "" "Last year, how much money did his relatives in this household earn working " "for wages or salary?" msgstr "" +"Geçen yıl, bu hanedeki akrabaları ücretli veya maaşlı çalışarak ne kadar " +"para kazandı?" #: Form/form_us.xml.h:957 msgid "Relatives in household earnings from own business in 1949" -msgstr "" +msgstr "1949'da hanedeki akrabaların kendi işlerinden elde ettiği kazanç" #: Form/form_us.xml.h:958 msgid "" "Last year, how much money did his relatives in this household earn in own " "business, professional practice, or farm?" msgstr "" +"Geçen yıl, bu hanedeki akrabaları kendi işleri, serbest meslekleri veya " +"çiftliklerinden ne kadar para kazandı?" #: Form/form_us.xml.h:959 msgid "Relatives in household other income in 1949" -msgstr "" +msgstr "1949'da hanedeki akrabaların diğer gelirleri" #: Form/form_us.xml.h:960 msgid "" "Last year, how much money did his relatives in this household receive from " "interest, dividends, veteran’s allowances, pensions, rents, or other income?" msgstr "" +"Geçen yıl, bu hanedeki akrabaları faiz, temettü, gazi ödenekleri, emekli " +"maaşları, kira gelirleri veya diğer gelirlerden ne kadar para aldı?" #: Form/form_us.xml.h:961 msgid "WWII Veteran?" -msgstr "" +msgstr "II. Dünya Savaşı gazisi mi?" #: Form/form_us.xml.h:962 msgid "Did he ever serve in the U. S. Armed Forces during World War II?" msgstr "" +"II. Dünya Savaşı sırasında ABD Silahlı Kuvvetlerinde hiç görev yaptı mı?" #: Form/form_us.xml.h:963 msgid "WWI Veteran?" -msgstr "" +msgstr "I. Dünya Savaşı gazisi mi?" #: Form/form_us.xml.h:964 msgid "World War I?" -msgstr "" +msgstr "I. Dünya Savaşı mı?" #: Form/form_us.xml.h:965 msgid "Any other time, including present service?" -msgstr "" +msgstr "Mevcut hizmet dâhil olmak üzere başka herhangi bir zamanda?" #: Form/form_us.xml.h:966 msgid "What kind of work did this person do in his last job?" -msgstr "" +msgstr "Bu kişi son işinde ne tür bir çalışma yaptı?" #: Form/form_us.xml.h:967 msgid "What kind of business or industry did he work in?" -msgstr "" +msgstr "Hangi iş kolunda veya sektörde çalıştı?" #: Form/form_us.xml.h:968 msgid "Class of worker [last job]" -msgstr "" +msgstr "Çalışanın sınıfı [son iş]" #: Form/form_us.xml.h:969 msgid "Has this person been married more than once?" -msgstr "" +msgstr "Bu kişi birden fazla kez evlendi mi?" #: Form/form_us.xml.h:970 msgid "# years since marriage ended" -msgstr "" +msgstr "# Evliliğin sona ermesinden bu yana geçen yıl sayısı" #: Form/form_us.xml.h:971 msgid "" "How many years since this person was married, widowed, divorced, or " "separated?" msgstr "" +"Bu kişinin evli, dul, boşanmış veya ayrı yaşar durumda olmasının üzerinden " +"kaç yıl geçti?" #: Form/form_us.xml.h:972 msgid "How many children has she ever borne, not counting stillbirths?" -msgstr "" +msgstr "Ölü doğumlar hariç, şimdiye kadar kaç çocuk doğurdu?" #: Form/form_us.xml.h:973 msgid "Census Date" -msgstr "" +msgstr "Nüfus Sayımı Tarihi" #: Form/form_us.xml.h:974 msgid "District of" -msgstr "" +msgstr "Bölgesi" #: Form/form_us.xml.h:977 msgid "Married Y/N" -msgstr "" +msgstr "Evli E/H" #: Form/form_us.xml.h:978 msgid "Spouses Age" -msgstr "" +msgstr "Eşlerin Yaşı" #: Form/form_us.xml.h:982 msgid "File No." -msgstr "" +msgstr "Dosya No." #: Form/form_us.xml.h:984 Form/form_us.xml.h:1003 Form/form_us.xml.h:1058 msgid "Post Office Address" -msgstr "" +msgstr "Posta Adresi" #: Form/form_us.xml.h:987 msgid "Entered service as (rank)" -msgstr "" +msgstr "Hizmete giriş (rütbe)" #: Form/form_us.xml.h:988 msgid "Date Entered" -msgstr "" +msgstr "Hizmete giriş tarihi" #: Form/form_us.xml.h:989 Form/form_us.xml.h:1010 msgid "Place of Enlistment" -msgstr "" +msgstr "Askere alınma yeri" #: Form/form_us.xml.h:990 msgid "Name, Letter of Co., Number of Regiment" -msgstr "" +msgstr "Birliğin adı, bölüğün harfi, alayın numarası" #: Form/form_us.xml.h:991 msgid "Continued until" -msgstr "" +msgstr "Devam ettiği tarih" #: Form/form_us.xml.h:992 msgid "Re-enlisted as" -msgstr "" +msgstr "Tekrar kayıt olduğu tarih" #: Form/form_us.xml.h:993 msgid "On date" -msgstr "" +msgstr "Şu tarihte" #: Form/form_us.xml.h:995 msgid "and continued until" -msgstr "" +msgstr "ve şu tarihe kadar devam etti" #: Form/form_us.xml.h:996 msgid "3rd enlistment" -msgstr "" +msgstr "3. askerlik kaydı" #: Form/form_us.xml.h:997 msgid "Enlisted on" -msgstr "" +msgstr "Şu tarihte askere alındı" #: Form/form_us.xml.h:998 msgid "Enlistment Place" -msgstr "" +msgstr "Askere alınma yeri" #: Form/form_us.xml.h:999 msgid "in the command of" -msgstr "" +msgstr "şu komutanlığın emrinde" #: Form/form_us.xml.h:1001 msgid "Other service, if any" -msgstr "" +msgstr "Varsa diğer hizmetleri" #: Form/form_us.xml.h:1004 msgid "No. on pension roll" -msgstr "" +msgstr "Emekli maaşı sicil numarası" #: Form/form_us.xml.h:1005 msgid "County lived in when first placed on roll" -msgstr "" +msgstr "İlk kez emekli maaşı siciline alındığında yaşadığı ilçe" #: Form/form_us.xml.h:1006 msgid "Other states lived in and when" -msgstr "" +msgstr "Yaşadığı diğer eyaletler ve ne zaman yaşadığı" #: Form/form_us.xml.h:1007 msgid "Moved to Alabama" -msgstr "" +msgstr "Alabama'ya taşındı" #: Form/form_us.xml.h:1012 msgid "Branch of service" -msgstr "" +msgstr "Hizmet sınıfı" #: Form/form_us.xml.h:1014 msgid "Letter of Company" -msgstr "" +msgstr "Bölük harfi" #: Form/form_us.xml.h:1015 msgid "Number of Regiment" -msgstr "" +msgstr "Alay numarası" #: Form/form_us.xml.h:1016 msgid "Was it an Alabama Regiment" -msgstr "" +msgstr "Bu bir Alabama Alayı mıydı" #: Form/form_us.xml.h:1017 msgid "If not, give name of state" -msgstr "" +msgstr "Değilse, eyaletin adını belirtin" #: Form/form_us.xml.h:1018 msgid "Name of your Captain" -msgstr "" +msgstr "Yüzbaşınızın adı" #: Form/form_us.xml.h:1019 msgid "Name of your Colonel" -msgstr "" +msgstr "Albayınızın adı" #: Form/form_us.xml.h:1020 msgid "By what other name was your Company called" -msgstr "" +msgstr "Bölüğünüz başka hangi adla anılıyordu" #: Form/form_us.xml.h:1021 msgid "By what other name was your Regiment called" -msgstr "" +msgstr "Alayınız başka hangi adla anılıyordu" #: Form/form_us.xml.h:1022 msgid "Battles in which you took part" -msgstr "" +msgstr "Katıldığınız muharebeler" #: Form/form_us.xml.h:1023 msgid "Ever wounded" -msgstr "" +msgstr "Hiç yaralandınız mı" #: Form/form_us.xml.h:1024 msgid "When wounded" -msgstr "" +msgstr "Ne zaman yaralandınız" #: Form/form_us.xml.h:1025 msgid "In what battles" -msgstr "" +msgstr "Hangi muharebelerde" #: Form/form_us.xml.h:1026 msgid "Were you captured" -msgstr "" +msgstr "Esir alındınız mı" #: Form/form_us.xml.h:1027 msgid "When captured" -msgstr "" +msgstr "Ne zaman esir alındınız" #: Form/form_us.xml.h:1028 msgid "Where captured" -msgstr "" +msgstr "Nerede esir alındınız" #: Form/form_us.xml.h:1029 msgid "Were you imprisoned" -msgstr "" +msgstr "Hapsedildiniz mi" #: Form/form_us.xml.h:1030 msgid "When imprisoned" -msgstr "" +msgstr "Ne zaman hapsedildiniz" #: Form/form_us.xml.h:1031 msgid "Where imprisoned" -msgstr "" +msgstr "Nerede hapsedildiniz" #: Form/form_us.xml.h:1032 msgid "How were you discharged" -msgstr "" +msgstr "Terhisiniz nasıl gerçekleşti" #: Form/form_us.xml.h:1033 msgid "Did you transfer to another Company or Command" -msgstr "" +msgstr "Başka bir Bölüğe veya Birliğe nakledildiniz mi" #: Form/form_us.xml.h:1034 msgid "If so, give Company and Regiment" -msgstr "" +msgstr "Evetse, Bölük ve Alayın adını belirtin" #: Form/form_us.xml.h:1035 msgid "What branch of service and date of transfer" -msgstr "" +msgstr "Hangi hizmet sınıfına ve nakil tarihi" #: Form/form_us.xml.h:1036 msgid "Captains name" -msgstr "" +msgstr "Yüzbaşının adı" #: Form/form_us.xml.h:1037 msgid "Colonels name" -msgstr "" +msgstr "Albayın adı" #: Form/form_us.xml.h:1038 msgid "Length of service" -msgstr "" +msgstr "Hizmet süresi" #: Form/form_us.xml.h:1039 msgid "Are you married" -msgstr "" +msgstr "Evli misiniz" #: Form/form_us.xml.h:1040 msgid "Age of wife" -msgstr "" +msgstr "Eşinizin yaşı" #: Form/form_us.xml.h:1041 msgid "Birthplace of wife" -msgstr "" +msgstr "Eşinizin doğum yeri" #: Form/form_us.xml.h:1042 msgid "Date married" -msgstr "" +msgstr "Evlenme tarihi" #: Form/form_us.xml.h:1043 msgid "Place married" @@ -12712,1203 +12770,1205 @@ msgstr "Evlenme yeri" #: Form/form_us.xml.h:1044 msgid "When, where and circumstances you quit the service" -msgstr "" +msgstr "Hizmetten ne zaman, nerede ve koşullarda ayrıldığınız" #: Form/form_us.xml.h:1045 msgid "Paroled at close of war" -msgstr "" +msgstr "Savaşın sonunda şartlı tahliye edildi" #: Form/form_us.xml.h:1046 msgid "If so, when" -msgstr "" +msgstr "Eğer öyleyse, ne zaman" #: Form/form_us.xml.h:1047 msgid "and where" -msgstr "" +msgstr "ve nerede" #: Form/form_us.xml.h:1048 msgid "Have you your parole" -msgstr "" +msgstr "Şartlı tahliye belgeniz var mı" #: Form/form_us.xml.h:1049 msgid "Voting Precinct and County" -msgstr "" +msgstr "Oy verme bölgesi ve ilçe" #: Form/form_us.xml.h:1051 msgid "With whom are you living" -msgstr "" +msgstr "Kiminle birlikte yaşıyorsunuz" #: Form/form_us.xml.h:1052 msgid "Regularly received your pension warrant" -msgstr "" +msgstr "Emekli maaşı ödemenizi düzenli olarak aldınız mı" #: Form/form_us.xml.h:1053 msgid "If No give information regarding the same" -msgstr "" +msgstr "Hayır ise, bununla ilgili bilgi verin" #: Form/form_us.xml.h:1056 msgid "Witness name and P.O Address" -msgstr "" +msgstr "Tanığın adı ve posta adresi" #: Form/form_us.xml.h:1061 Form/form_us.xml.h:2621 msgid "Widow of" -msgstr "" +msgstr "Dul eşi" #: Form/form_us.xml.h:1062 msgid "Her age" -msgstr "" +msgstr "Yaşı" #: Form/form_us.xml.h:1063 msgid "Her date of birth" -msgstr "" +msgstr "Doğum tarihi" #: Form/form_us.xml.h:1064 msgid "Date of marriage" -msgstr "" +msgstr "Evlenme tarihi" #: Form/form_us.xml.h:1074 msgid "Born within census year" -msgstr "" +msgstr "Nüfus sayımı yılı içinde doğdu" #: Form/form_us.xml.h:1077 msgid "Married during census year" -msgstr "" +msgstr "Nüfus sayımı yılı içinde evlendi" #: Form/form_us.xml.h:1079 msgid "Months unemployed during census year" -msgstr "" +msgstr "Nüfus sayımı yılı içinde işsiz kaldığı ay sayısı" #: Form/form_us.xml.h:1080 msgid "(Day of the Enumerator's visit) sick or temporarily disabled" -msgstr "" +msgstr "(Sayım görevlisinin ziyareti günü) hasta veya geçici olarak engelli" #: Form/form_us.xml.h:1082 Form/form_us.xml.h:1213 msgid "Deaf and Dumb" -msgstr "" +msgstr "Sağır ve dilsiz" #: Form/form_us.xml.h:1085 msgid "Maimed, Crippled, Bedridden, or otherwise disabled." -msgstr "" +msgstr "Sakat, engelli, yatağa bağımlı veya başka bir şekilde engelli." #: Form/form_us.xml.h:1087 msgid "Can not read" -msgstr "" +msgstr "Okuyamıyor" #: Form/form_us.xml.h:1088 msgid "Can not write" -msgstr "" +msgstr "Yazamıyor" #: Form/form_us.xml.h:1103 msgid "Street Name" -msgstr "" +msgstr "Sokak Adı" #: Form/form_us.xml.h:1104 msgid "House No." -msgstr "" +msgstr "Ev No." #: Form/form_us.xml.h:1130 Form/form_us.xml.h:1145 msgid "Precinct No." -msgstr "" +msgstr "Bölge No." #: Form/form_us.xml.h:1136 msgid "Inside or Outside City Limits" -msgstr "" +msgstr "Şehir Sınırları İçinde veya Dışında" #: Form/form_us.xml.h:1137 Form/form_us.xml.h:1153 msgid "Age Male" -msgstr "" +msgstr "Erkek Yaşı" #: Form/form_us.xml.h:1138 Form/form_us.xml.h:1154 msgid "Age Female" -msgstr "" +msgstr "Kadın Yaşı" #: Form/form_us.xml.h:1140 msgid "Relation to Family" -msgstr "" +msgstr "Aile ile İlişkisi" #: Form/form_us.xml.h:1142 Form/form_us.xml.h:1156 msgid "Degree of Education" -msgstr "" +msgstr "Eğitim Düzeyi" #: Form/form_us.xml.h:1143 msgid "Owner or Renter" -msgstr "" +msgstr "Ev Sahibi veya Kiracı" #: Form/form_us.xml.h:1152 msgid "In or Out" -msgstr "" +msgstr "İçeride veya Dışarıda" #: Form/form_us.xml.h:1159 msgid "White Males Under 10" -msgstr "" +msgstr "10 Yaş Altı Beyaz Erkekler" #: Form/form_us.xml.h:1160 msgid "White Males 10 to 20" -msgstr "" +msgstr "10–20 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1161 msgid "White Males 20 to 30" -msgstr "" +msgstr "20–30 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1162 msgid "White Males 30 to 40" -msgstr "" +msgstr "30–40 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1163 msgid "White Males 40 to 50" -msgstr "" +msgstr "40–50 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1164 msgid "White Males 50 to 60" -msgstr "" +msgstr "50–60 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1165 msgid "White Males 60 to 70" -msgstr "" +msgstr "60–70 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1166 msgid "White Males 70 to 80" -msgstr "" +msgstr "70–80 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1167 msgid "White Males 80 to 90" -msgstr "" +msgstr "80–90 Yaş Arası Beyaz Erkekler" #: Form/form_us.xml.h:1168 msgid "White Males Over 90" -msgstr "" +msgstr "90 Yaş Üstü Beyaz Erkekler" #: Form/form_us.xml.h:1169 msgid "White Females Under 10" -msgstr "" +msgstr "10 Yaş Altı Beyaz Kadınlar" #: Form/form_us.xml.h:1170 msgid "White Females 10 to 20" -msgstr "" +msgstr "10–20 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1171 msgid "White Females 20 to 30" -msgstr "" +msgstr "20–30 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1172 msgid "White Females 30 to 40" -msgstr "" +msgstr "30–40 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1173 msgid "White Females 40 to 50" -msgstr "" +msgstr "40–50 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1174 msgid "White Females 50 to 60" -msgstr "" +msgstr "50–60 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1175 msgid "White Females 60 to 70" -msgstr "" +msgstr "60–70 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1176 msgid "White Females 70 to 80" -msgstr "" +msgstr "70–80 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1177 msgid "White Females 80 to 90" -msgstr "" +msgstr "80–90 Yaş Arası Beyaz Kadınlar" #: Form/form_us.xml.h:1178 msgid "White Females Over 90" -msgstr "" +msgstr "90 Yaş Üstü Beyaz Kadınlar" #: Form/form_us.xml.h:1179 msgid "Male Negroes and Mulattoes" -msgstr "" +msgstr "Siyahi ve Melez Erkekler" #: Form/form_us.xml.h:1180 msgid "Female Negroes and Mulattoes" -msgstr "" +msgstr "Siyahi ve Melez Kadınlar" #: Form/form_us.xml.h:1182 Form/form_us.xml.h:1212 msgid "Militia" -msgstr "" +msgstr "Milisler" #: Form/form_us.xml.h:1183 msgid "Manufactories of All Kinds" -msgstr "" +msgstr "Her Türlü İmalathane" #: Form/form_us.xml.h:1184 msgid "Value of products of manufactories" -msgstr "" +msgstr "İmalathane ürünlerinin değeri" #: Form/form_us.xml.h:1185 msgid "Value of products of Coal Mines" -msgstr "" +msgstr "Kömür madeni ürünlerinin değeri" #: Form/form_us.xml.h:1187 msgid "Pounds of Wool" -msgstr "" +msgstr "Yün miktarı, pound olarak" #: Form/form_us.xml.h:1188 msgid "Number of Colleges" -msgstr "" +msgstr "Kolej Sayısı" #: Form/form_us.xml.h:1189 Form/form_us.xml.h:1191 msgid "Number of Pupils" -msgstr "" +msgstr "Öğrenci Sayısı" #: Form/form_us.xml.h:1190 msgid "Number of Common Schools" -msgstr "" +msgstr "Devlet Okulu Sayısı" #: Form/form_us.xml.h:1194 msgid "Males over 21 years" -msgstr "" +msgstr "21 yaş üstü erkekler" #: Form/form_us.xml.h:1195 msgid "Males under 21 years" -msgstr "" +msgstr "21 yaş altı erkekler" #: Form/form_us.xml.h:1197 Form/form_us.xml.h:1199 msgid "Total number" -msgstr "" +msgstr "Toplam sayı" #: Form/form_us.xml.h:1205 Form/form_us.xml.h:1887 Form/form_us.xml.h:1911 #: Form/form_us.xml.h:1938 Form/form_us.xml.h:2065 Form/form_us.xml.h:2101 #: Form/form_us.xml.h:2139 Form/form_us.xml.h:2177 Form/form_us.xml.h:2215 msgid "Widowed" -msgstr "" +msgstr "Dullar" #: Form/form_us.xml.h:1206 msgid "Years resident in the state" -msgstr "" +msgstr "Eyalette ikamet edilen yıl sayısı" #: Form/form_us.xml.h:1209 msgid "Native voters" -msgstr "" +msgstr "Yerli seçmenler" #: Form/form_us.xml.h:1210 msgid "Naturalized voters" -msgstr "" +msgstr "Vatandaşlığa alınmış seçmenler" #: Form/form_us.xml.h:1217 msgid "Owners of land" -msgstr "" +msgstr "Arazi sahipleri" #: Form/form_us.xml.h:1218 msgid "Paupers" -msgstr "" +msgstr "Yoksullar" #: Form/form_us.xml.h:1219 msgid "Acres of improved land" -msgstr "" +msgstr "İşlenmiş arazi alanı, dönüm olarak" #: Form/form_us.xml.h:1220 msgid "Acres of unimproved land" -msgstr "" +msgstr "İşlenmemiş arazi alanı, dönüm olarak" #: Form/form_us.xml.h:1221 msgid "Acres in meadow" -msgstr "" +msgstr "Çayır alanı, dönüm olarak" #: Form/form_us.xml.h:1222 msgid "Tons of hay" -msgstr "" +msgstr "Ton cinsinden saman" #: Form/form_us.xml.h:1223 msgid "Bushels of grass seed" -msgstr "" +msgstr "Çim tohumu, kile olarak" #: Form/form_us.xml.h:1224 msgid "Acres of spring wheat" -msgstr "" +msgstr "İlkbahar buğdayı alanı, dönüm olarak" #: Form/form_us.xml.h:1225 msgid "Bushels harvested (spring wheat)" -msgstr "" +msgstr "Hasat edilen ilkbahar buğdayı, kile olarak" #: Form/form_us.xml.h:1226 msgid "Acres of winter wheat" -msgstr "" +msgstr "Kış buğdayı alanı, dönüm olarak" #: Form/form_us.xml.h:1227 msgid "Bushels harvested (winter wheat)" -msgstr "" +msgstr "Hasat edilen buğday miktarı (kışlık buğday)" #: Form/form_us.xml.h:1228 msgid "Acres of oats" -msgstr "" +msgstr "Yulaf ekili arazi, dönüm olarak" #: Form/form_us.xml.h:1229 msgid "Bushels harvested (oats)" -msgstr "" +msgstr "Hasat edilen yulaf, kile olarak" #: Form/form_us.xml.h:1230 msgid "Acres of corn" -msgstr "" +msgstr "Mısır ekili arazi, dönüm olarak" #: Form/form_us.xml.h:1231 msgid "Bushels harvested (corn)" -msgstr "" +msgstr "Hasat edilen mısır, kile olarak" #: Form/form_us.xml.h:1232 msgid "Acres of potatoes" -msgstr "" +msgstr "Patates ekili arazi, dönüm olarak" #: Form/form_us.xml.h:1233 msgid "Bushels harvested (potatoes)" -msgstr "" +msgstr "Hasat edilen kile (patates)" #: Form/form_us.xml.h:1234 msgid "Number of hogs sold" -msgstr "" +msgstr "Satılan domuz sayısı" #: Form/form_us.xml.h:1235 msgid "Value of hogs sold" -msgstr "" +msgstr "Satılan domuzların değeri" #: Form/form_us.xml.h:1236 msgid "Number of cattle sold" -msgstr "" +msgstr "Satılan sığır sayısı" #: Form/form_us.xml.h:1237 msgid "Value of cattle sold" -msgstr "" +msgstr "Satılan sığırların değeri" #: Form/form_us.xml.h:1238 msgid "Pounds of butter manufactured" -msgstr "" +msgstr "Üretilen tereyağı, pound olarak" #: Form/form_us.xml.h:1239 msgid "Pounds of cheese" -msgstr "" +msgstr "Peynir, pound olarak" #: Form/form_us.xml.h:1240 msgid "Pounds of wool" -msgstr "" +msgstr "Yün, pound olarak" #: Form/form_us.xml.h:1241 msgid "Value of domestic manufactures" -msgstr "" +msgstr "Yerli imalat ürünlerinin değeri" #: Form/form_us.xml.h:1242 msgid "Value of general manufactures" -msgstr "" +msgstr "Genel imalat ürünlerinin değeri" #: Form/form_us.xml.h:1245 Form/form_us.xml.h:1274 Form/form_us.xml.h:1622 #: Form/form_us.xml.h:1873 msgid "Town of" -msgstr "" +msgstr "Kasabası" #: Form/form_us.xml.h:1246 Form/form_us.xml.h:1273 Form/form_us.xml.h:1874 #: Form/form_us.xml.h:1961 msgid "City of" -msgstr "" +msgstr "Şehri" #: Form/form_us.xml.h:1247 Form/form_us.xml.h:1275 Form/form_us.xml.h:1876 msgid "Township of" -msgstr "" +msgstr "İlçesi" #: Form/form_us.xml.h:1253 msgid "Street and Number" -msgstr "" +msgstr "Sokak ve Numara" #: Form/form_us.xml.h:1259 msgid "Place of birth, county" -msgstr "" +msgstr "Doğum yeri, ilçe" #: Form/form_us.xml.h:1260 msgid "Place of birth, state or territory" -msgstr "" +msgstr "Doğum yeri, eyalet veya bölge" #: Form/form_us.xml.h:1261 msgid "Place of birth, country" -msgstr "" +msgstr "Doğum yeri, ülke" #: Form/form_us.xml.h:1262 msgid "Father (N, Native; F, Foreign)" -msgstr "" +msgstr "Baba (Y, Yerli; E, Ecnebi)" #: Form/form_us.xml.h:1263 msgid "Mother (N, Native; F, Foreign)" -msgstr "" +msgstr "Anne (Y, Yerli; E, Ecnebi)" #: Form/form_us.xml.h:1264 Form/form_us.xml.h:1292 msgid "Subject to military duty" -msgstr "" +msgstr "Askerlik görevine tabi" #: Form/form_us.xml.h:1265 Form/form_us.xml.h:1293 msgid "Entitled to vote" -msgstr "" +msgstr "Oy kullanma hakkına sahip" #: Form/form_us.xml.h:1266 msgid "Alien who has taken out first papers" -msgstr "" +msgstr "İlk vatandaşlık başvurusunu yapmış yabancı" #: Form/form_us.xml.h:1267 msgid "Alien who has not taken out first papers" -msgstr "" +msgstr "İlk vatandaşlık başvurusunu yapmamış yabancı" #: Form/form_us.xml.h:1268 msgid "Over 10 and cannot read or write" -msgstr "" +msgstr "10 yaşından büyük ve okuma yazma bilmiyor" #: Form/form_us.xml.h:1269 msgid "Over 10 and can read but not write" -msgstr "" +msgstr "10 yaşından büyük ve okuyabiliyor ama yazamıyor" #: Form/form_us.xml.h:1270 msgid "Deaf and dumb, blind, insane, or idiotic" -msgstr "" +msgstr "Sağır ve dilsiz, kör, akıl hastası veya zihinsel engelli" #: Form/form_us.xml.h:1278 msgid "Age 18 or older" -msgstr "" +msgstr "18 yaş veya üzeri" #: Form/form_us.xml.h:1279 msgid "Age 5 to 18" -msgstr "" +msgstr "5-18 yaş arası" #: Form/form_us.xml.h:1280 msgid "Age under 5" -msgstr "" +msgstr "5 yaşın altında" #: Form/form_us.xml.h:1281 msgid "White Male" -msgstr "" +msgstr "Beyaz Erkek" #: Form/form_us.xml.h:1282 msgid "White Female" -msgstr "" +msgstr "Beyaz Kadın" #: Form/form_us.xml.h:1283 msgid "Colored Male" -msgstr "" +msgstr "Renkli Erkek" #: Form/form_us.xml.h:1284 msgid "Colored Female" -msgstr "" +msgstr "Renkli Kadın" #: Form/form_us.xml.h:1286 msgid "Single, Widowed, or Divorced" -msgstr "" +msgstr "Bekâr, Dul veya Boşanmış" #: Form/form_us.xml.h:1288 msgid "Father (N,Native; F,Foreign)" -msgstr "" +msgstr "Baba (Y, Yerli; E, Ecnebi)" #: Form/form_us.xml.h:1289 msgid "Mother (N,Native; F,Foreign)" -msgstr "" +msgstr "Anne (Y, Yerli; E, Ecnebi)" #: Form/form_us.xml.h:1291 msgid "Religious belief" -msgstr "" +msgstr "Dini inanç" #: Form/form_us.xml.h:1294 msgid "Can read but not write, over 10 years old" -msgstr "" +msgstr "Okuyabiliyor ama yazamıyor, 10 yaşından büyük" #: Form/form_us.xml.h:1295 msgid "Cannot read or write, over 10 years old" -msgstr "" +msgstr "Okuyamıyor veya yazamıyor, 10 yaşından büyük" #: Form/form_us.xml.h:1296 msgid "Children over 6 and under 17 not attending school in 1894" msgstr "" +"1894 yılında okula devam etmeyen, 6 yaşından büyük ve 17 yaşından küçük " +"çocuklar" #: Form/form_us.xml.h:1298 msgid "Births in 1894" -msgstr "" +msgstr "1894 yılındaki doğumlar" #: Form/form_us.xml.h:1299 msgid "Deaths in 1894" -msgstr "" +msgstr "1894 yılındaki ölümler" #: Form/form_us.xml.h:1300 msgid "Deaf and dumb not in State School for Deaf" -msgstr "" +msgstr "Devlet Sağırlar Okulunda olmayan sağır ve dilsizler" #: Form/form_us.xml.h:1301 msgid "Blind not in State College for Blind" -msgstr "" +msgstr "Devlet Körler Kolejinde olmayan körler" #: Form/form_us.xml.h:1302 msgid "Insane not in State Hospital for Insane" -msgstr "" +msgstr "Devlet Akıl Hastanesinde olmayan akıl hastaları" #: Form/form_us.xml.h:1303 msgid "Company if in Civil War" -msgstr "" +msgstr "İç Savaşta görev yaptıysa birliği" #: Form/form_us.xml.h:1304 msgid "Regiment if in Civil War" -msgstr "" +msgstr "İç Savaşta görev yaptıysa alayı" #: Form/form_us.xml.h:1305 msgid "State if in Civil War" -msgstr "" +msgstr "İç Savaşta görev yaptıysa eyaleti" #: Form/form_us.xml.h:1306 msgid "Arm of service and rank if in Civil War" -msgstr "" +msgstr "İç Savaşta görev yaptıysa hizmet sınıfı ve rütbesi" #: Form/form_us.xml.h:1307 msgid "Regiment if in Mexican War" -msgstr "" +msgstr "Meksika Savaşında görev yaptıysa alayı" #: Form/form_us.xml.h:1308 msgid "State if in Mexican War" -msgstr "" +msgstr "Meksika Savaşında görev yaptıysa eyaleti" #: Form/form_us.xml.h:1309 Form/form_us.xml.h:1376 Form/form_us.xml.h:2038 #: Form/form_us.xml.h:2075 Form/form_us.xml.h:2112 Form/form_us.xml.h:2150 #: Form/form_us.xml.h:2188 msgid "Card No." -msgstr "" +msgstr "Kart No." #: Form/form_us.xml.h:1316 msgid "P.O. Address" -msgstr "" +msgstr "Posta Kutusu Adresi" #: Form/form_us.xml.h:1317 msgid "P. O. Address" -msgstr "" +msgstr "Posta Kutusu Adresi" #: Form/form_us.xml.h:1321 msgid "Can you read?" -msgstr "" +msgstr "Okuyabiliyor musunuz?" #: Form/form_us.xml.h:1323 msgid "Can you write?" -msgstr "" +msgstr "Yazabiliyor musunuz?" #: Form/form_us.xml.h:1325 msgid "Color, White-Black-Yellow-Red" -msgstr "" +msgstr "Renk: Beyaz-Siyah-Sarı-Kırmızı" #: Form/form_us.xml.h:1328 msgid "Place of Birth-Self" -msgstr "" +msgstr "Doğum Yeri-Kendisi" #: Form/form_us.xml.h:1329 msgid "Place of birth, Self" -msgstr "" +msgstr "Doğum yeri, Kendisi" #: Form/form_us.xml.h:1330 msgid "Place of Birth-Mother" -msgstr "" +msgstr "Doğum Yeri-Annesi" #: Form/form_us.xml.h:1331 msgid "Place of birth, Mother" -msgstr "" +msgstr "Doğum yeri, Annesi" #: Form/form_us.xml.h:1332 msgid "Place of Birth-Father" -msgstr "" +msgstr "Doğum Yeri-Babası" #: Form/form_us.xml.h:1333 msgid "Place of birth, Father" -msgstr "" +msgstr "Doğum yeri, Babası" #: Form/form_us.xml.h:1334 msgid "Own home or farm" -msgstr "" +msgstr "Kendi evi veya çiftliği" #: Form/form_us.xml.h:1335 msgid "Do you own your home or farm?" -msgstr "" +msgstr "Ev veya çiftlik sahibi misiniz?" #: Form/form_us.xml.h:1336 msgid "Value of home or farm" -msgstr "" +msgstr "Ev veya çiftliğin değeri" #: Form/form_us.xml.h:1337 msgid "Entire value of home or farm?" -msgstr "" +msgstr "Ev veya çiftliğin toplam değeri?" #: Form/form_us.xml.h:1338 msgid "How much incumberance on home or farm" -msgstr "" +msgstr "Ev veya çiftlik üzerindeki ipotek miktarı" #: Form/form_us.xml.h:1339 msgid "How much incumberance on your home or farm?" -msgstr "" +msgstr "Ev veya çiftliğinizde ne kadar ipotek var?" #: Form/form_us.xml.h:1340 msgid "If foreign born, are you naturalized" -msgstr "" +msgstr "Yabancı doğumluysanız, vatandaşlığa kabul edildiniz mi" #: Form/form_us.xml.h:1341 msgid "If you are foreign born, are you naturalized?" -msgstr "" +msgstr "Yabancı doğumluysanız, vatandaşlığa kabul edildiniz mi?" #: Form/form_us.xml.h:1342 Form/form_us.xml.h:1343 Form/form_us.xml.h:1421 #: Form/form_us.xml.h:2110 Form/form_us.xml.h:2148 Form/form_us.xml.h:2186 #: Form/form_us.xml.h:2224 msgid "Years in U.S." -msgstr "" +msgstr "ABD'de geçirilen yıllar." #: Form/form_us.xml.h:1344 Form/form_us.xml.h:1422 Form/form_us.xml.h:1440 msgid "Years in Iowa" -msgstr "" +msgstr "Iowa'da geçirilen yıllar" #: Form/form_us.xml.h:1345 msgid "Years in Iowa>" -msgstr "" +msgstr "Iowa'da geçirilen yıllar>" #: Form/form_us.xml.h:1347 msgid "Conjugal condition; Single-Married-Widowed-Divorced-Separated" -msgstr "" +msgstr "Medeni durum; Bekâr-Evli-Dul-Boşanmış-Ayrı" #: Form/form_us.xml.h:1348 msgid "Months in school in 1904-Public" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı-Devlet Okulu" #: Form/form_us.xml.h:1349 msgid "Months in school in 1904; Public" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı; Devlet Okulu" #: Form/form_us.xml.h:1350 msgid "Months in school in 1904-High" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı-Lise" #: Form/form_us.xml.h:1351 msgid "Months in school in 1904; High" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı; Lise" #: Form/form_us.xml.h:1352 msgid "Months in school in 1904-Private" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı-Özel Okul" #: Form/form_us.xml.h:1353 msgid "Months in school in 1904; Private" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı; Özel Okul" #: Form/form_us.xml.h:1354 msgid "Months in school in 1904-College" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı-Kolej" #: Form/form_us.xml.h:1355 msgid "Months in school in 1904; College" -msgstr "" +msgstr "1904 yılında okulda geçirilen ay sayısı; Kolej" #: Form/form_us.xml.h:1358 Form/form_us.xml.h:1359 msgid "Months unemployed in 1904" -msgstr "" +msgstr "1904 yılında işsiz geçirilen ay sayısı" #: Form/form_us.xml.h:1360 msgid "Military-Service in which war" -msgstr "" +msgstr "Askerlik Hizmeti; hangi savaşta" #: Form/form_us.xml.h:1361 msgid "Military Service in; Civil War-Mexican War-Spanish War" -msgstr "" +msgstr "Askerlik Hizmeti; İç Savaş-Meksika Savaşı-İspanyol Savaşı" #: Form/form_us.xml.h:1362 msgid "Military-Company" -msgstr "" +msgstr "Askerî Birlik" #: Form/form_us.xml.h:1363 msgid "Military Service; Company" -msgstr "" +msgstr "Askerlik Hizmeti; Birlik" #: Form/form_us.xml.h:1364 msgid "Military-Regiment" -msgstr "" +msgstr "Askeri Alay" #: Form/form_us.xml.h:1365 msgid "Military Service; Regiment" -msgstr "" +msgstr "Askerlik Hizmeti; Alay" #: Form/form_us.xml.h:1366 msgid "Military-State" -msgstr "" +msgstr "Askerlik-Eyalet" #: Form/form_us.xml.h:1367 msgid "Military Service; State" -msgstr "" +msgstr "Askerlik Hizmeti; Eyalet" #: Form/form_us.xml.h:1368 msgid "Military-Class of service" -msgstr "" +msgstr "Askeri Hizmet Sınıfı" #: Form/form_us.xml.h:1369 msgid "Military Service; Class of service; Cavalry-Infantry-Artillery-Navy" -msgstr "" +msgstr "Askerlik Hizmeti; Hizmet sınıfı; Süvari-Piyade-Topçu-Donanma" #: Form/form_us.xml.h:1370 msgid "Military-Date of enlistment" -msgstr "" +msgstr "Askerlik Kayıt Tarihi" #: Form/form_us.xml.h:1371 msgid "Military Service; Date of enlistment" -msgstr "" +msgstr "Askerlik Hizmeti; Kayıt tarihi" #: Form/form_us.xml.h:1372 msgid "Military-Date of discharge" -msgstr "" +msgstr "Askerlik-Terhis tarihi" #: Form/form_us.xml.h:1373 msgid "Military Service; Date of discharge" -msgstr "" +msgstr "Askerlik Hizmeti; Terhis tarihi" #: Form/form_us.xml.h:1374 msgid "Military-Remarks" -msgstr "" +msgstr "Askerlik-Açıklamalar" #: Form/form_us.xml.h:1375 msgid "Military Service; Remarks" -msgstr "" +msgstr "Askerlik Hizmeti; Açıklamalar" #: Form/form_us.xml.h:1381 Form/form_us.xml.h:2080 Form/form_us.xml.h:2117 #: Form/form_us.xml.h:2155 Form/form_us.xml.h:2193 msgid "Town or Township" -msgstr "" +msgstr "Kasaba veya İlçe" #: Form/form_us.xml.h:1384 msgid "Months in 1914 Unemployed" -msgstr "" +msgstr "1914'te İşsiz Geçirilen Ay Sayısı" #: Form/form_us.xml.h:1385 msgid "Earnings for 1914 from occupation" -msgstr "" +msgstr "1914 Yılında Meslekten Elde Edilen Kazanç" #: Form/form_us.xml.h:1386 msgid "Extent of Education Common" -msgstr "" +msgstr "İlkokul Eğitim Düzeyi" #: Form/form_us.xml.h:1387 msgid "Extent of Education Grammar" -msgstr "" +msgstr "Ortaokul Eğitim Düzeyi" #: Form/form_us.xml.h:1388 msgid "Extent of Education High School" -msgstr "" +msgstr "Lise Eğitim Düzeyi" #: Form/form_us.xml.h:1389 msgid "Extent of Education College" -msgstr "" +msgstr "Üniversite Eğitim Düzeyi" #: Form/form_us.xml.h:1391 msgid "Own house or farm" -msgstr "" +msgstr "Kendi evi veya çiftliği" #: Form/form_us.xml.h:1392 msgid "Incumbrance on farm or home" -msgstr "" +msgstr "Çiftlik veya ev üzerinde ipotek" #: Form/form_us.xml.h:1393 msgid "Value of farm or home" -msgstr "" +msgstr "Çiftliğin veya evin değeri" #: Form/form_us.xml.h:1394 msgid "Military Service: Civil War" -msgstr "" +msgstr "Askerlik Hizmeti: İç Savaş" #: Form/form_us.xml.h:1395 msgid "Military Service: Mexican" -msgstr "" +msgstr "Askerlik Hizmeti: Meksika" #: Form/form_us.xml.h:1396 msgid "Military Service: Spanish" -msgstr "" +msgstr "Askerlik Hizmeti: İspanya" #: Form/form_us.xml.h:1397 msgid "Military Service: Infantry" -msgstr "" +msgstr "Askerlik Hizmeti: Piyade" #: Form/form_us.xml.h:1398 msgid "Military Service: Cavalry" -msgstr "" +msgstr "Askerlik Hizmeti: Süvari" #: Form/form_us.xml.h:1399 msgid "Military Service: Artillery" -msgstr "" +msgstr "Askerlik Hizmeti: Topçu" #: Form/form_us.xml.h:1400 msgid "Military Service: Navy" -msgstr "" +msgstr "Askerlik Hizmeti: Donanma" #: Form/form_us.xml.h:1401 msgid "Military Service: State" -msgstr "" +msgstr "Askerlik Hizmeti: Eyalet" #: Form/form_us.xml.h:1402 msgid "Military Service: Regiment" -msgstr "" +msgstr "Askerlik Hizmeti: Alay" #: Form/form_us.xml.h:1403 msgid "Military Service: Company" -msgstr "" +msgstr "Askerlik Hizmeti: Bölük" #: Form/form_us.xml.h:1404 msgid "Church Affiliation" -msgstr "" +msgstr "Bağlı Olduğu Kilise" #: Form/form_us.xml.h:1405 Form/form_us.xml.h:2450 Form/form_us.xml.h:2473 msgid "Father's Birthplace" -msgstr "" +msgstr "Babanın Doğum Yeri" #: Form/form_us.xml.h:1406 Form/form_us.xml.h:2452 Form/form_us.xml.h:2475 msgid "Mother's Birthplace" -msgstr "" +msgstr "Annenin Doğum Yeri" #: Form/form_us.xml.h:1410 msgid "Months in School 1914: Public" -msgstr "" +msgstr "1914'te Okulda Geçirilen Ay Sayısı: Devlet Okulu" #: Form/form_us.xml.h:1411 msgid "Months in School 1914: High" -msgstr "" +msgstr "1914'te Okulda Geçirilen Ay Sayısı: Lise" #: Form/form_us.xml.h:1412 msgid "Months in School 1914: Private" -msgstr "" +msgstr "1914'te Okulda Geçirilen Ay Sayısı: Özel Okul" #: Form/form_us.xml.h:1413 msgid "Months in School 1914: College" -msgstr "" +msgstr "1914'te Okulda Geçirilen Ay Sayısı: Üniversite" #: Form/form_us.xml.h:1417 Form/form_us.xml.h:2072 Form/form_us.xml.h:2106 #: Form/form_us.xml.h:2144 Form/form_us.xml.h:2182 Form/form_us.xml.h:2220 msgid "Deaf" -msgstr "" +msgstr "Sağır" #: Form/form_us.xml.h:1419 Form/form_us.xml.h:2108 Form/form_us.xml.h:2146 #: Form/form_us.xml.h:2184 Form/form_us.xml.h:2222 msgid "Idiot" -msgstr "" +msgstr "Ahmak" #: Form/form_us.xml.h:1420 msgid "Naturalized if foreign born?" -msgstr "" +msgstr "Yabancı doğumluysa vatandaşlığa alınmış mı?" #: Form/form_us.xml.h:1429 Form/form_us.xml.h:2332 msgid "Color or Race" -msgstr "" +msgstr "Renk veya Irk" #: Form/form_us.xml.h:1432 msgid "Home Owned or Rented" -msgstr "" +msgstr "Ev Sahibi veya Kiracı" #: Form/form_us.xml.h:1433 msgid "Home Owned Free or Mortgaged" -msgstr "" +msgstr "Ev Borçsuz veya İpotekli" #: Form/form_us.xml.h:1434 msgid "Home Value" -msgstr "" +msgstr "Evin Değeri" #: Form/form_us.xml.h:1435 msgid "Mortgage Debt" -msgstr "" +msgstr "İpotek Borcu" #: Form/form_us.xml.h:1436 msgid "Monthly Rent" -msgstr "" +msgstr "Aylık Kira" #: Form/form_us.xml.h:1437 msgid "Insurance on Home" -msgstr "" +msgstr "Ev Sigortası" #: Form/form_us.xml.h:1439 msgid "Years in US" -msgstr "" +msgstr "ABD'de Geçirilen Yıllar" #: Form/form_us.xml.h:1441 msgid "Attended Rural School" -msgstr "" +msgstr "Kırsal Okula Devam Etti" #: Form/form_us.xml.h:1442 msgid "Attended Grade School" -msgstr "" +msgstr "İlkokula Devam Etti" #: Form/form_us.xml.h:1443 msgid "Attended High School" -msgstr "" +msgstr "Liseye Devam Etti" #: Form/form_us.xml.h:1444 msgid "Attended College" -msgstr "" +msgstr "Üniversiteye Devam Etti" #: Form/form_us.xml.h:1445 msgid "Highest Grade Rural School" -msgstr "" +msgstr "Kırsal Okulda Tamamlanan En Yüksek Sınıf" #: Form/form_us.xml.h:1446 msgid "Highest Grade Grammar School" -msgstr "" +msgstr "İlkokulda Tamamlanan En Yüksek Sınıf" #: Form/form_us.xml.h:1447 msgid "Highest Grade High School" -msgstr "" +msgstr "Lisede Tamamlanan En Yüksek Sınıf" #: Form/form_us.xml.h:1448 msgid "No. Years College" -msgstr "" +msgstr "Üniversitede Okunan Yıllar" #: Form/form_us.xml.h:1449 msgid "Months in School 1924" -msgstr "" +msgstr "1924'te Okulda Geçirilen Aylar" #: Form/form_us.xml.h:1450 msgid "Able to Read" -msgstr "" +msgstr "Okuyabiliyor" #: Form/form_us.xml.h:1451 msgid "Able to Write" -msgstr "" +msgstr "Yazabiliyor" #: Form/form_us.xml.h:1454 msgid "Father's Place of Birth" -msgstr "" +msgstr "Babanın Doğum Yeri" #: Form/form_us.xml.h:1457 msgid "Mother's Place of Birth" -msgstr "" +msgstr "Annenin Doğum Yeri" #: Form/form_us.xml.h:1459 msgid "Parents Place of Marriage" -msgstr "" +msgstr "Ebeveynlerin Evlilik Yeri" #: Form/form_us.xml.h:1460 msgid "Civil War Veteran" -msgstr "" +msgstr "İç Savaş Gazisi" #: Form/form_us.xml.h:1461 msgid "Civil War Service Branch" -msgstr "" +msgstr "İç Savaş Hizmet Kolu" #: Form/form_us.xml.h:1462 msgid "Civil War State" -msgstr "" +msgstr "İç Savaş Eyaleti" #: Form/form_us.xml.h:1463 msgid "Spanish-American War Veteran" -msgstr "" +msgstr "İspanyol-Amerikan Savaşı Gazisi" #: Form/form_us.xml.h:1464 msgid "Spanish-American War Service Branch" -msgstr "" +msgstr "İspanyol-Amerikan Savaşı Hizmet Kolu" #: Form/form_us.xml.h:1465 msgid "Spanish-American War State" -msgstr "" +msgstr "İspanyol-Amerikan Savaşı Eyaleti" #: Form/form_us.xml.h:1466 msgid "World War Veteran" -msgstr "" +msgstr "Dünya Savaşı Gazisi" #: Form/form_us.xml.h:1467 msgid "World War Service Branch" -msgstr "" +msgstr "Dünya Savaşı Hizmet Kolu" #: Form/form_us.xml.h:1468 msgid "World War State" -msgstr "" +msgstr "Dünya Savaşı Eyaleti" #: Form/form_us.xml.h:1469 msgid "Agricultural Pursuits" -msgstr "" +msgstr "Tarımsal Uğraşlar" #: Form/form_us.xml.h:1470 msgid "Professional Services" -msgstr "" +msgstr "Profesyonel Hizmetler" #: Form/form_us.xml.h:1471 msgid "Domestic/Personal Services" -msgstr "" +msgstr "Ev İçi/Kişisel Hizmetler" #: Form/form_us.xml.h:1472 msgid "Trade and Transportation" -msgstr "" +msgstr "Ticaret ve Ulaştırma" #: Form/form_us.xml.h:1473 msgid "Manufacturing and Mechanical" -msgstr "" +msgstr "İmalat ve Mekanik İşler" #: Form/form_us.xml.h:1474 msgid "Unclassified Laborer" -msgstr "" +msgstr "Sınıflandırılmamış İşçi" #: Form/form_us.xml.h:1475 msgid "Months unemployed due to illness" -msgstr "" +msgstr "Hastalık nedeniyle işsiz kalınan aylar" #: Form/form_us.xml.h:1476 msgid "Income lost due to illness" -msgstr "" +msgstr "Hastalık nedeniyle kaybedilen gelir" #: Form/form_us.xml.h:1477 msgid "Months unemployed" -msgstr "" +msgstr "İşsiz kalınan aylar" #: Form/form_us.xml.h:1479 msgid "Free Inhabitants in" -msgstr "" +msgstr "Özgür Sakinler" #: Form/form_us.xml.h:1488 Form/form_us.xml.h:1535 Form/form_us.xml.h:2092 #: Form/form_us.xml.h:2130 Form/form_us.xml.h:2168 Form/form_us.xml.h:2206 msgid "Regiment" -msgstr "" +msgstr "Alay" #: Form/form_us.xml.h:1491 Form/form_us.xml.h:1503 msgid "Real Estate" -msgstr "" +msgstr "Gayrimenkul" #: Form/form_us.xml.h:1492 msgid "Personal Estate" -msgstr "" +msgstr "Kişisel Mal Varlığı" #: Form/form_us.xml.h:1495 msgid "Attended School" -msgstr "" +msgstr "Okula Devam Etti" #: Form/form_us.xml.h:1496 msgid "Illiterate" -msgstr "" +msgstr "Okuma Yazma Bilmiyor" #: Form/form_us.xml.h:1504 msgid "Personal Property" -msgstr "" +msgstr "Kişisel Mülkiyet" #: Form/form_us.xml.h:1506 Form/form_us.xml.h:1526 msgid "Where From" -msgstr "" +msgstr "Nereden" #: Form/form_us.xml.h:1507 Form/form_us.xml.h:1528 msgid "School" -msgstr "" +msgstr "Okul" #: Form/form_us.xml.h:1508 msgid "Cannot Read 10-15" -msgstr "" +msgstr "10-15 Yaş Arası Okuyamayanlar" #: Form/form_us.xml.h:1509 msgid "Cannot Read 15-21" -msgstr "" +msgstr "15-21 Yaş Arası Okuyamayanlar" #: Form/form_us.xml.h:1510 msgid "Cannot Read 21+" -msgstr "" +msgstr "21 Yaş ve Üzeri Okuyamayanlar" #: Form/form_us.xml.h:1511 msgid "Cannot Write 10-15" -msgstr "" +msgstr "10-15 Yaş Arası Yazamayanlar" #: Form/form_us.xml.h:1512 msgid "Cannot Write 15-21" -msgstr "" +msgstr "15-21 Yaş Arası Yazamayanlar" #: Form/form_us.xml.h:1513 msgid "Cannot Write 21+" -msgstr "" +msgstr "21 Yaş ve Üzeri Yazamayanlar" #: Form/form_us.xml.h:1514 msgid "Cannot Read/Write 10-15" -msgstr "" +msgstr "10-15 Yaş Arası Okuyamayanlar/Yazamayanlar" #: Form/form_us.xml.h:1515 msgid "Cannot Read/Write 15-21" -msgstr "" +msgstr "15-21 Yaş Arası Okuyamayanlar/Yazamayanlar" #: Form/form_us.xml.h:1516 msgid "Cannot Read/Write 21+" -msgstr "" +msgstr "21 Yaş ve Üzeri Okuyamayanlar/Yazamayanlar" #: Form/form_us.xml.h:1523 msgid "Widow(er)" -msgstr "" +msgstr "Dul(lar)" #: Form/form_us.xml.h:1527 msgid "Learning" -msgstr "" +msgstr "Öğrenim" #: Form/form_us.xml.h:1529 msgid "Illiteracy 10-15" -msgstr "" +msgstr "10-15 Yaş Arası Okuma Yazma Bilmeyenler" #: Form/form_us.xml.h:1530 msgid "Illiteracy 15-21" -msgstr "" +msgstr "15-21 Yaş Arası Okuma Yazma Bilmeyenler" #: Form/form_us.xml.h:1531 msgid "Illiteracy 21+" -msgstr "" +msgstr "21 Yaş ve Üzeri Okuma Yazma Bilmeyenler" #: Form/form_us.xml.h:1532 msgid "Honorably discharged" -msgstr "" +msgstr "Şerefli terhis" #: Form/form_us.xml.h:1533 msgid "State Enlisted" -msgstr "" +msgstr "Askere yazıldığı eyalet" #: Form/form_us.xml.h:1536 msgid "Arm of Service" -msgstr "" +msgstr "Hizmet sınıfı" #: Form/form_us.xml.h:1537 msgid "Prison" -msgstr "" +msgstr "Hapishane" #: Form/form_us.xml.h:1543 msgid "Where from Kansas" -msgstr "" +msgstr "Kansas'tan nereli" #: Form/form_us.xml.h:1545 Form/form_us.xml.h:1567 Form/form_us.xml.h:1589 msgid "Trade or profession being learned" -msgstr "" +msgstr "Öğrenilmekte olan zanaat veya meslek" #: Form/form_us.xml.h:1547 Form/form_us.xml.h:1569 Form/form_us.xml.h:1591 msgid "Illiterate and 10 to 15 years old" -msgstr "" +msgstr "Okuma Yazma Bilmeyen ve 10-15 Yaş Arası" #: Form/form_us.xml.h:1548 msgid "Illiterate and 16 to 21 years old" -msgstr "" +msgstr "Okuma Yazma Bilmeyen ve 16-21 Yaş Arası" #: Form/form_us.xml.h:1549 Form/form_us.xml.h:1571 Form/form_us.xml.h:1593 msgid "Illiterate and over 21" -msgstr "" +msgstr "Okuma Yazma Bilmeyen ve 21 Yaş Üzeri" #: Form/form_us.xml.h:1550 Form/form_us.xml.h:1572 Form/form_us.xml.h:1595 msgid "Honorably discharged from military service" -msgstr "" +msgstr "Askerlik hizmetinden şerefli terhis" #: Form/form_us.xml.h:1551 Form/form_us.xml.h:1573 Form/form_us.xml.h:1596 msgid "State in which enlisted" -msgstr "" +msgstr "Askere yazıldığı eyalet" #: Form/form_us.xml.h:1552 Form/form_us.xml.h:1574 Form/form_us.xml.h:1597 msgid "Company or command" -msgstr "" +msgstr "Bölük veya komutanlık" #: Form/form_us.xml.h:1553 Form/form_us.xml.h:1575 Form/form_us.xml.h:1598 msgid "Regiment or other organization" -msgstr "" +msgstr "Alay veya diğer birlik" #: Form/form_us.xml.h:1554 Form/form_us.xml.h:1576 Form/form_us.xml.h:1599 msgid "Arm of service" -msgstr "" +msgstr "Hizmet sınıfı" #: Form/form_us.xml.h:1555 Form/form_us.xml.h:1577 Form/form_us.xml.h:1600 msgid "Prison in which confined as a prisoner of war" -msgstr "" +msgstr "Savaş esiri olarak tutulduğu hapishane" #: Form/form_us.xml.h:1558 Form/form_us.xml.h:1580 Form/form_us.xml.h:1606 msgid "Home owned free or mortgaged" -msgstr "" +msgstr "Ev kendisine ait ve borçsuz veya ipotekli" #: Form/form_us.xml.h:1564 Form/form_us.xml.h:1586 Form/form_us.xml.h:1612 msgid "Where from to Kansas" -msgstr "" +msgstr "Kansas'a nereden geldi" #: Form/form_us.xml.h:1566 Form/form_us.xml.h:1588 msgid "Number of months unemployed" -msgstr "" +msgstr "İşsiz kaldığı ay sayısı" #: Form/form_us.xml.h:1570 Form/form_us.xml.h:1592 msgid "Illiterate and 15 to 21 years old" -msgstr "" +msgstr "Okuma yazma bilmeyen ve 15-21 yaş arası" #: Form/form_us.xml.h:1594 msgid "Number of volumes in home library" @@ -13916,916 +13976,918 @@ msgstr "Ev kütüphanesindeki cilt sayısı" #: Form/form_us.xml.h:1603 msgid "Number of house" -msgstr "" +msgstr "Ev numarası" #: Form/form_us.xml.h:1613 msgid "Year of immigration to the United States" -msgstr "" +msgstr "Amerika Birleşik Devletleri'ne göç ettiği yıl" #: Form/form_us.xml.h:1614 msgid "Naturalized, alien or first papers" msgstr "" +"Vatandaşlığa kabul edilmiş, yabancı uyruklu veya ilk vatandaşlık belgelerini " +"almış" #: Form/form_us.xml.h:1618 msgid "Attended school within year" -msgstr "" +msgstr "Yıl içinde okula devam etti" #: Form/form_us.xml.h:1628 Form/form_us.xml.h:1969 Form/form_us.xml.h:1988 #: Form/form_us.xml.h:2008 msgid "Nativity" -msgstr "" +msgstr "Doğum yeri" #: Form/form_us.xml.h:1629 msgid "Father's Nativity" -msgstr "" +msgstr "Babanın doğum yeri" #: Form/form_us.xml.h:1630 msgid "Mother's Nativity" -msgstr "" +msgstr "Annenin doğum yeri" #: Form/form_us.xml.h:1634 Form/form_us.xml.h:1649 msgid "Inhabitants in" -msgstr "" +msgstr "İkamet edenler" #: Form/form_us.xml.h:1646 msgid "Served in Federal army during rebellion" -msgstr "" +msgstr "İsyan sırasında Federal orduda hizmet etti" #: Form/form_us.xml.h:1658 Form/form_us.xml.h:1689 msgid "Residence in State - Years" -msgstr "" +msgstr "Eyalette ikamet süresi - Yıllar" #: Form/form_us.xml.h:1659 Form/form_us.xml.h:1690 msgid "Residence in State - Months" -msgstr "" +msgstr "Eyalette ikamet süresi - Aylar" #: Form/form_us.xml.h:1660 msgid "Residence in District - Years" -msgstr "" +msgstr "İlçede ikamet süresi - Yıllar" #: Form/form_us.xml.h:1661 msgid "Residence in District - Months" -msgstr "" +msgstr "İlçede ikamet - Aylar" #: Form/form_us.xml.h:1663 Form/form_us.xml.h:2339 msgid "Months Employed" -msgstr "" +msgstr "Çalışılan Aylar" #: Form/form_us.xml.h:1664 msgid "Solder/Sailor" -msgstr "" +msgstr "Lehimci/Denizci" #: Form/form_us.xml.h:1665 msgid "Foreign father" -msgstr "" +msgstr "Yabancı baba" #: Form/form_us.xml.h:1666 msgid "Foreign mother" -msgstr "" +msgstr "Yabancı anne" #: Form/form_us.xml.h:1669 msgid "Organized Township" -msgstr "" +msgstr "Organize İlçe" #: Form/form_us.xml.h:1670 msgid "Unorganized Township" -msgstr "" +msgstr "Organize Olmayan İlçe" #: Form/form_us.xml.h:1671 msgid "Name of Village" -msgstr "" +msgstr "Köyün adı" #: Form/form_us.xml.h:1672 msgid "Name of City" -msgstr "" +msgstr "Şehrin adı" #: Form/form_us.xml.h:1674 msgid "Sub-division" -msgstr "" +msgstr "Alt bölge" #: Form/form_us.xml.h:1675 msgid "Precincts" -msgstr "" +msgstr "Seçim bölgeleri" #: Form/form_us.xml.h:1676 msgid "Enumerated date from" -msgstr "" +msgstr "Sayım başlangıç tarihi" #: Form/form_us.xml.h:1677 msgid "Enumerated date to" -msgstr "" +msgstr "Sayım bitiş tarihi" #: Form/form_us.xml.h:1680 msgid "Enumeration number" -msgstr "" +msgstr "Sayım numarası" #: Form/form_us.xml.h:1687 msgid "Fathers birthplace" -msgstr "" +msgstr "Babanın doğum yeri" #: Form/form_us.xml.h:1688 msgid "Mothers birthplace" -msgstr "" +msgstr "Annenin doğum yeri" #: Form/form_us.xml.h:1691 msgid "Residence in District - years" -msgstr "" +msgstr "Bölgede ikamet süresi - yıllar" #: Form/form_us.xml.h:1692 msgid "Residence in District - months" -msgstr "" +msgstr "Bölgede ikamet süresi - aylar" #: Form/form_us.xml.h:1694 msgid "Army service" -msgstr "" +msgstr "Askerlik hizmeti" #: Form/form_us.xml.h:1695 msgid "Wars" -msgstr "" +msgstr "Savaşlar" #: Form/form_us.xml.h:1697 Form/form_us.xml.h:1770 msgid "White males under 10" -msgstr "" +msgstr "10 yaş altı beyaz erkekler" #: Form/form_us.xml.h:1698 Form/form_us.xml.h:1771 msgid "White males 10-17" -msgstr "" +msgstr "10-17 yaş arası beyaz erkekler" #: Form/form_us.xml.h:1699 Form/form_us.xml.h:1772 msgid "White males 18-20" -msgstr "" +msgstr "18-20 yaş arası beyaz erkekler" #: Form/form_us.xml.h:1700 Form/form_us.xml.h:1773 msgid "White males 21-44" -msgstr "" +msgstr "21-44 yaş arası beyaz erkekler" #: Form/form_us.xml.h:1701 Form/form_us.xml.h:1774 msgid "White males 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri beyaz erkekler" #: Form/form_us.xml.h:1702 Form/form_us.xml.h:1775 msgid "Total White Males" -msgstr "" +msgstr "Toplam beyaz erkek" #: Form/form_us.xml.h:1703 Form/form_us.xml.h:1776 msgid "White females under 10" -msgstr "" +msgstr "10 yaş altı beyaz kadınlar" #: Form/form_us.xml.h:1704 Form/form_us.xml.h:1777 msgid "White females 10-17" -msgstr "" +msgstr "10-17 yaş arası beyaz kadınlar" #: Form/form_us.xml.h:1705 Form/form_us.xml.h:1778 msgid "White females 18-20" -msgstr "" +msgstr "18-20 yaş arası beyaz kadınlar" #: Form/form_us.xml.h:1706 Form/form_us.xml.h:1779 msgid "White females 21-44" -msgstr "" +msgstr "21-44 yaş arası beyaz kadınlar" #: Form/form_us.xml.h:1707 Form/form_us.xml.h:1780 msgid "White females 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri beyaz kadınlar" #: Form/form_us.xml.h:1708 Form/form_us.xml.h:1781 msgid "Total White Females" -msgstr "" +msgstr "Toplam beyaz kadın" #: Form/form_us.xml.h:1709 Form/form_us.xml.h:1782 msgid "Total White Population" -msgstr "" +msgstr "Toplam beyaz nüfus" #: Form/form_us.xml.h:1710 Form/form_us.xml.h:1783 msgid "White persons between 6 and 18" -msgstr "" +msgstr "6-18 yaş arasındaki beyaz kişiler" #: Form/form_us.xml.h:1711 Form/form_us.xml.h:1784 msgid "No. White persons who can read and write" -msgstr "" +msgstr "Okuyup yazabilen beyaz kişilerin sayısı" #: Form/form_us.xml.h:1712 Form/form_us.xml.h:1785 msgid "Colored males under 10" -msgstr "" +msgstr "10 yaş altı renkli erkekler" #: Form/form_us.xml.h:1713 Form/form_us.xml.h:1786 msgid "Colored males 10-17" -msgstr "" +msgstr "10-17 yaş arası renkli erkekler" #: Form/form_us.xml.h:1714 Form/form_us.xml.h:1787 msgid "Colored males 18-20" -msgstr "" +msgstr "18-20 yaş arası renkli erkekler" #: Form/form_us.xml.h:1715 Form/form_us.xml.h:1788 msgid "Colored males 21-44" -msgstr "" +msgstr "21-44 yaş arası renkli erkekler" #: Form/form_us.xml.h:1716 Form/form_us.xml.h:1789 msgid "Colored males 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri renkli erkekler" #: Form/form_us.xml.h:1717 Form/form_us.xml.h:1790 msgid "Total Colored Males" -msgstr "" +msgstr "Toplam renkli erkek" #: Form/form_us.xml.h:1718 Form/form_us.xml.h:1791 msgid "Colored females under 10" -msgstr "" +msgstr "10 yaş altı renkli kadınlar" #: Form/form_us.xml.h:1719 Form/form_us.xml.h:1792 msgid "Colored females 10-17" -msgstr "" +msgstr "10-17 yaş arası renkli kadınlar" #: Form/form_us.xml.h:1720 Form/form_us.xml.h:1793 msgid "Colored females 18-20" -msgstr "" +msgstr "18-20 yaş arası renkli kadınlar" #: Form/form_us.xml.h:1721 Form/form_us.xml.h:1794 msgid "Colored females 21-44" -msgstr "" +msgstr "21-44 yaş arası renkli kadınlar" #: Form/form_us.xml.h:1722 Form/form_us.xml.h:1795 msgid "Colored females 45 and over" -msgstr "" +msgstr "45 yaş ve üzeri renkli kadınlar" #: Form/form_us.xml.h:1723 Form/form_us.xml.h:1796 msgid "Total Colored Females" -msgstr "" +msgstr "Toplam renkli kadın" #: Form/form_us.xml.h:1724 Form/form_us.xml.h:1797 msgid "Total Colored Population" -msgstr "" +msgstr "Toplam renkli nüfus" #: Form/form_us.xml.h:1725 Form/form_us.xml.h:1798 msgid "Colored persons between 6 and 18" -msgstr "" +msgstr "6-18 yaş arası renkli kişiler" #: Form/form_us.xml.h:1726 Form/form_us.xml.h:1799 msgid "No. Colored persons who can read and write" -msgstr "" +msgstr "Okuma ve yazma bilen renkli kişi sayısı" #: Form/form_us.xml.h:1727 Form/form_us.xml.h:1800 msgid "Deaf and Dumb Males" -msgstr "" +msgstr "Sağır ve dilsiz erkekler" #: Form/form_us.xml.h:1728 Form/form_us.xml.h:1801 msgid "Deaf and Dumb Males Age" -msgstr "" +msgstr "Sağır ve dilsiz erkeklerin yaşı" #: Form/form_us.xml.h:1729 Form/form_us.xml.h:1802 msgid "Deaf and Dumb Females" -msgstr "" +msgstr "Sağır ve dilsiz kadınlar" #: Form/form_us.xml.h:1730 Form/form_us.xml.h:1803 msgid "Deaf and Dumb Females Age" -msgstr "" +msgstr "Sağır ve dilsiz kadınların yaşı" #: Form/form_us.xml.h:1731 Form/form_us.xml.h:1804 msgid "Deaf and Dumb White" -msgstr "" +msgstr "Sağır ve Dilsiz Beyaz" #: Form/form_us.xml.h:1732 Form/form_us.xml.h:1805 msgid "Deaf and Dumb Colored" -msgstr "" +msgstr "Sağır ve Dilsiz Renkli" #: Form/form_us.xml.h:1733 Form/form_us.xml.h:1806 msgid "No. Deaf and Dumb taught to read and write" -msgstr "" +msgstr "Okuma ve yazma öğretilmiş sağır ve dilsiz sayısı" #: Form/form_us.xml.h:1734 Form/form_us.xml.h:1807 msgid "Blind Males" -msgstr "" +msgstr "Kör Erkekler" #: Form/form_us.xml.h:1735 Form/form_us.xml.h:1808 msgid "Blind Males Age" -msgstr "" +msgstr "Kör Erkeklerin Yaşı" #: Form/form_us.xml.h:1736 Form/form_us.xml.h:1809 msgid "Blind Females" -msgstr "" +msgstr "Kör Kadınlar" #: Form/form_us.xml.h:1737 Form/form_us.xml.h:1810 msgid "Blind Females Age" -msgstr "" +msgstr "Kör Kadınların Yaşı" #: Form/form_us.xml.h:1738 Form/form_us.xml.h:1811 msgid "Blind White" -msgstr "" +msgstr "Kör Beyazlar" #: Form/form_us.xml.h:1739 Form/form_us.xml.h:1812 msgid "Blind Colored" -msgstr "" +msgstr "Kör Renkliler" #: Form/form_us.xml.h:1740 Form/form_us.xml.h:1813 msgid "No. Blind taught to read and write" -msgstr "" +msgstr "Okuma ve yazma öğretilmiş kör sayısı" #: Form/form_us.xml.h:1741 Form/form_us.xml.h:1814 msgid "Insane Males" -msgstr "" +msgstr "Akıl Hastası Erkekler" #: Form/form_us.xml.h:1742 Form/form_us.xml.h:1815 msgid "Insane Males Age" -msgstr "" +msgstr "Akıl Hastası Erkeklerin Yaşı" #: Form/form_us.xml.h:1743 Form/form_us.xml.h:1816 msgid "Insane Females" -msgstr "" +msgstr "Akıl Hastası Kadınlar" #: Form/form_us.xml.h:1744 Form/form_us.xml.h:1817 msgid "Insane Females Age" -msgstr "" +msgstr "Akıl Hastası Kadınların Yaşı" #: Form/form_us.xml.h:1745 Form/form_us.xml.h:1818 msgid "Insane White" -msgstr "" +msgstr "Akıl Hastası Beyazlar" #: Form/form_us.xml.h:1746 Form/form_us.xml.h:1819 msgid "Insane Colored" -msgstr "" +msgstr "Akıl Hastası Renkliler" #: Form/form_us.xml.h:1747 Form/form_us.xml.h:1820 msgid "No. Insane taught to read and write" -msgstr "" +msgstr "Okuma ve yazma öğretilmiş akıl hastası sayısı" #: Form/form_us.xml.h:1748 Form/form_us.xml.h:1821 msgid "Live Stock Horses" -msgstr "" +msgstr "Canlı Hayvanlık Atlar" #: Form/form_us.xml.h:1749 Form/form_us.xml.h:1822 msgid "Live Stock Mules" -msgstr "" +msgstr "Canlı Hayvanlık Katırlar" #: Form/form_us.xml.h:1750 Form/form_us.xml.h:1823 msgid "Live Stock Jacks" -msgstr "" +msgstr "Canlı Hayvanlık Eşekler" #: Form/form_us.xml.h:1751 Form/form_us.xml.h:1824 msgid "Live Stock Jennies" -msgstr "" +msgstr "Canlı Hayvanlık Dişi Katırlar" #: Form/form_us.xml.h:1752 Form/form_us.xml.h:1825 msgid "Live Stock Cattle" -msgstr "" +msgstr "Canlı Hayvanlık Sığırlar" #: Form/form_us.xml.h:1753 Form/form_us.xml.h:1826 msgid "Live Stock Sheep" -msgstr "" +msgstr "Canlı Hayvanlık Koyunlar" #: Form/form_us.xml.h:1754 Form/form_us.xml.h:1827 msgid "Live Stock Hogs" -msgstr "" +msgstr "Canlı Hayvanlık Domuzlar" #: Form/form_us.xml.h:1755 Form/form_us.xml.h:1828 msgid "Products Bushels Wheat" -msgstr "" +msgstr "Ürünler Buğday, kile olarak" #: Form/form_us.xml.h:1756 Form/form_us.xml.h:1829 msgid "Products Bushels Corn" -msgstr "" +msgstr "Ürünler Mısır, kile olarak" #: Form/form_us.xml.h:1757 Form/form_us.xml.h:1830 msgid "Products Bushels Oats" -msgstr "" +msgstr "Ürünler Yulaf, kile olarak" #: Form/form_us.xml.h:1758 Form/form_us.xml.h:1831 msgid "Products Bushels Barley" -msgstr "" +msgstr "Ürünler Çavdar, kile olarak" #: Form/form_us.xml.h:1759 Form/form_us.xml.h:1832 msgid "Products Bushels Rye" -msgstr "" +msgstr "Ürünler Çavdar, kile olarak" #: Form/form_us.xml.h:1760 Form/form_us.xml.h:1833 msgid "Products Lbs. Tobacco" -msgstr "" +msgstr "Ürünler Tütün, kile olarak" #: Form/form_us.xml.h:1761 Form/form_us.xml.h:1834 msgid "Products Lbs. Wool" -msgstr "" +msgstr "Ürünler Yün, kile olarak" #: Form/form_us.xml.h:1762 Form/form_us.xml.h:1835 msgid "Products Lbs. Sugar" -msgstr "" +msgstr "Ürünler Şeker, kile olarak" #: Form/form_us.xml.h:1763 Form/form_us.xml.h:1836 msgid "Products Tons Hay" -msgstr "" +msgstr "Ürünler Saman, kile olarak" #: Form/form_us.xml.h:1764 Form/form_us.xml.h:1837 msgid "Products Tons Hemp" -msgstr "" +msgstr "Ürünler Kenevir, kile olarak" #: Form/form_us.xml.h:1765 Form/form_us.xml.h:1838 msgid "Products Galls. Whiskey" -msgstr "" +msgstr "Ürünler Viski, galon olarak" #: Form/form_us.xml.h:1766 Form/form_us.xml.h:1839 msgid "Products Galls. Wine" -msgstr "" +msgstr "Ürünler Şarap, galon olarak" #: Form/form_us.xml.h:1767 Form/form_us.xml.h:1840 msgid "Products Galls. Molasses" -msgstr "" +msgstr "Ürünler Pekmez, galon olarak" #: Form/form_us.xml.h:1768 Form/form_us.xml.h:1841 msgid "Total Population" -msgstr "" +msgstr "Toplam Nüfus" #: Form/form_us.xml.h:1879 Form/form_us.xml.h:1901 Form/form_us.xml.h:1930 msgid "Building Material" -msgstr "" +msgstr "Yapı Malzemesi" #: Form/form_us.xml.h:1880 Form/form_us.xml.h:1902 Form/form_us.xml.h:1931 msgid "Dwelling Value" -msgstr "" +msgstr "Konut Değeri" #: Form/form_us.xml.h:1885 Form/form_us.xml.h:1907 Form/form_us.xml.h:1936 msgid "Birthplace (NY County or other)" -msgstr "" +msgstr "Doğum Yeri (New York ilçesi veya başka bir yer)" #: Form/form_us.xml.h:1888 msgid "Years resident in this city or town" -msgstr "" +msgstr "Bu şehir veya kasabada ikamet edilen yıllar" #: Form/form_us.xml.h:1890 Form/form_us.xml.h:1915 Form/form_us.xml.h:1942 msgid "Native Voter" -msgstr "" +msgstr "Vatandaşlığa Geçirilmiş Seçmen" #: Form/form_us.xml.h:1891 Form/form_us.xml.h:1916 Form/form_us.xml.h:1943 msgid "Naturalized Voter" -msgstr "" +msgstr "Vatandaşlığa Alınmış Seçmen" #: Form/form_us.xml.h:1892 Form/form_us.xml.h:1917 Form/form_us.xml.h:1944 msgid "Alien" -msgstr "" +msgstr "Yabancı" #: Form/form_us.xml.h:1893 Form/form_us.xml.h:1918 msgid "Persons of color not taxed" -msgstr "" +msgstr "Vergiye tabi olmayan renkli kişiler" #: Form/form_us.xml.h:1894 Form/form_us.xml.h:1920 Form/form_us.xml.h:1946 msgid "Over 21/Illiterate" -msgstr "" +msgstr "21 Yaş Üstü/Okuma Yazma Bilmeyen" #: Form/form_us.xml.h:1895 Form/form_us.xml.h:1945 msgid "Landowner" -msgstr "" +msgstr "Toprak Sahibi" #: Form/form_us.xml.h:1896 Form/form_us.xml.h:1921 Form/form_us.xml.h:1947 msgid "Deaf, Dumb, Blind, Insane, Idiotic" -msgstr "" +msgstr "Sağır, Dilsiz, Kör, Akıl Hastası, Zihinsel Engelli" #: Form/form_us.xml.h:1897 Form/form_us.xml.h:1926 Form/form_us.xml.h:1948 #: Form/form_us.xml.h:2269 Form/form_us.xml.h:2298 Form/form_us.xml.h:2326 msgid "Inhabitants in the" -msgstr "" +msgstr "Yerleşik kişiler" #: Form/form_us.xml.h:1908 msgid "Parent of how many children" -msgstr "" +msgstr "Kaç çocuğun ebeveyni olduğu" #: Form/form_us.xml.h:1909 msgid "Number of times married" -msgstr "" +msgstr "Evlilik sayısı" #: Form/form_us.xml.h:1914 msgid "Place of Occupation" -msgstr "" +msgstr "Meslek yeri" #: Form/form_us.xml.h:1919 msgid "Land owner" -msgstr "" +msgstr "Arazi sahibi" #: Form/form_us.xml.h:1922 msgid "Now in army" -msgstr "" +msgstr "Şu anda orduda" #: Form/form_us.xml.h:1923 msgid "Now in navy" -msgstr "" +msgstr "Şu anda donanmada" #: Form/form_us.xml.h:1924 msgid "Formerly in army" -msgstr "" +msgstr "Daha önce orduda" #: Form/form_us.xml.h:1925 msgid "Formerly in navy" -msgstr "" +msgstr "Daha önce donanmada" #: Form/form_us.xml.h:1941 msgid "Usual place of employment (if not same city/town)" -msgstr "" +msgstr "Olağan çalışma yeri (aynı şehir/kasabada değilse)" #: Form/form_us.xml.h:1949 msgid "Election District of the Town of" -msgstr "" +msgstr "Kasabasının Seçim Bölgesi" #: Form/form_us.xml.h:1950 Form/form_us.xml.h:1962 Form/form_us.xml.h:2271 #: Form/form_us.xml.h:2300 Form/form_us.xml.h:2328 msgid "the County of" -msgstr "" +msgstr "İlçesi" #: Form/form_us.xml.h:1956 msgid "Birth Country" -msgstr "" +msgstr "Doğum Ülkesi" #: Form/form_us.xml.h:1957 Form/form_us.xml.h:1971 Form/form_us.xml.h:1990 #: Form/form_us.xml.h:2010 msgid "Citizen or Alien" -msgstr "" +msgstr "Vatandaş veya Yabancı" #: Form/form_us.xml.h:1959 msgid "Inhabitants living in" -msgstr "" +msgstr "Yaşayan sakinler" #: Form/form_us.xml.h:1960 msgid "Election District, Ward" -msgstr "" +msgstr "Seçim Bölgesi, Mahalle" #: Form/form_us.xml.h:1970 Form/form_us.xml.h:1989 Form/form_us.xml.h:2009 msgid "Number of years in the US" -msgstr "" +msgstr "ABD'de geçirilen yıl sayısı" #: Form/form_us.xml.h:1973 Form/form_us.xml.h:1992 Form/form_us.xml.h:2013 msgid "Class" -msgstr "" +msgstr "Sınıf" #: Form/form_us.xml.h:1974 Form/form_us.xml.h:1993 Form/form_us.xml.h:2014 msgid "Inmate: Residence given when admitted" -msgstr "" +msgstr "Kurum sakini: Kabul edildiğinde verilen ikamet adresi" #: Form/form_us.xml.h:1975 Form/form_us.xml.h:1996 msgid "Inhabitants of Block No." -msgstr "" +msgstr "Blok No. sakinleri." #: Form/form_us.xml.h:1976 Form/form_us.xml.h:1997 msgid "Election District No." -msgstr "" +msgstr "Seçim Bölgesi No." #: Form/form_us.xml.h:1977 Form/form_us.xml.h:1998 msgid "Ward No." -msgstr "" +msgstr "Mahalle No." #: Form/form_us.xml.h:1978 Form/form_us.xml.h:1999 msgid "City or Village" -msgstr "" +msgstr "Şehir veya Köy" #: Form/form_us.xml.h:1980 Form/form_us.xml.h:2001 msgid "Assembly District No." -msgstr "" +msgstr "Meclis Bölgesi No." #: Form/form_us.xml.h:2011 msgid "Where and When Naturalized" -msgstr "" +msgstr "Vatandaşlığa Kabul Edilme Yeri ve Zamanı" #: Form/form_us.xml.h:2017 msgid "WM 21-60" -msgstr "" +msgstr "21-60 Yaş Arası Erkek" #: Form/form_us.xml.h:2018 msgid "WM under 21 above 60 " -msgstr "" +msgstr "21 Yaş Altı ve 60 Yaş Üstü Erkek " #: Form/form_us.xml.h:2019 msgid "WF all ages" -msgstr "" +msgstr "Her Yaştan Kadın" #: Form/form_us.xml.h:2020 msgid "Blacks 12-50" -msgstr "" +msgstr "12-50 Yaş Arası Siyahlar" #: Form/form_us.xml.h:2021 msgid "Blacks under 12 above 50" -msgstr "" +msgstr "12 Yaş Altı ve 50 Yaş Üstü Siyahlar" #: Form/form_us.xml.h:2023 msgid "Native White Males" -msgstr "" +msgstr "Yerli Beyaz Erkekler" #: Form/form_us.xml.h:2024 msgid "Native White Females" -msgstr "" +msgstr "Yerli Beyaz Kadınlar" #: Form/form_us.xml.h:2025 msgid "Native Colored Males" -msgstr "" +msgstr "Yerli Renkli Erkekler" #: Form/form_us.xml.h:2026 msgid "Native Colored Females" -msgstr "" +msgstr "Yerli Renkli Kadınlar" #: Form/form_us.xml.h:2027 msgid "Foreign Males" -msgstr "" +msgstr "Yabancı Erkekler" #: Form/form_us.xml.h:2028 msgid "Foreign Females" -msgstr "" +msgstr "Yabancı Kadınlar" #: Form/form_us.xml.h:2029 msgid "Male Children 5 and Under" -msgstr "" +msgstr "5 Yaş ve Altındaki Erkek Çocuklar" #: Form/form_us.xml.h:2030 msgid "Female Children 5 and Under" -msgstr "" +msgstr "5 Yaş ve Altındaki Kız Çocuklar" #: Form/form_us.xml.h:2031 msgid "Males 5 to 20 Years of Age" -msgstr "" +msgstr "5 ile 20 Yaş Arasındaki Erkekler" #: Form/form_us.xml.h:2032 msgid "Females 5 to 20 Years of Age" -msgstr "" +msgstr "5 ile 20 Yaş Arasındaki Kadınlar" #: Form/form_us.xml.h:2033 msgid "Males 20 to 60 Years of Age" -msgstr "" +msgstr "20 ile 60 Yaş Arasındaki Erkekler" #: Form/form_us.xml.h:2034 msgid "Females 20 to 60 Years of Age" -msgstr "" +msgstr "20 ile 60 Yaş Arasındaki Kadınlar" #: Form/form_us.xml.h:2035 msgid "Males Over 60 Years of Age" -msgstr "" +msgstr "60 Yaş Üstü Erkekler" #: Form/form_us.xml.h:2036 msgid "Females Over 60 Years of Age" -msgstr "" +msgstr "60 Yaş Üstü Kadınlar" #: Form/form_us.xml.h:2040 Form/form_us.xml.h:2079 Form/form_us.xml.h:2116 #: Form/form_us.xml.h:2154 Form/form_us.xml.h:2192 msgid "P.O." -msgstr "" +msgstr "Posta Kodu" #: Form/form_us.xml.h:2042 msgid "Sec." -msgstr "" +msgstr "Bölüm" #: Form/form_us.xml.h:2043 msgid "T." -msgstr "" +msgstr "T." #: Form/form_us.xml.h:2044 msgid "R." -msgstr "" +msgstr "R." #: Form/form_us.xml.h:2047 Form/form_us.xml.h:2520 msgid "No." -msgstr "" +msgstr "No." #: Form/form_us.xml.h:2048 msgid "St./Ave " -msgstr "" +msgstr "Cad./Bulv. " #: Form/form_us.xml.h:2049 msgid "Hotel or Institution" -msgstr "" +msgstr "Otel veya Kurum" #: Form/form_us.xml.h:2053 msgid "Years in South Dakota" -msgstr "" +msgstr "Güney Dakota'da geçirilen yıllar" #: Form/form_us.xml.h:2054 msgid "Years in United States" -msgstr "" +msgstr "Amerika Birleşik Devletleri'nde geçirilen yıllar" #: Form/form_us.xml.h:2055 msgid "Birthplace of Father" -msgstr "" +msgstr "Babanın Doğum Yeri" #: Form/form_us.xml.h:2056 msgid "Birthplace of Mother" -msgstr "" +msgstr "Annenin Doğum Yeri" #: Form/form_us.xml.h:2060 msgid "Black" -msgstr "" +msgstr "Siyah" #: Form/form_us.xml.h:2061 msgid "Red" -msgstr "" +msgstr "Kırmızı" #: Form/form_us.xml.h:2062 msgid "Yellow" -msgstr "" +msgstr "Sarı" #: Form/form_us.xml.h:2066 Form/form_us.xml.h:2102 Form/form_us.xml.h:2140 #: Form/form_us.xml.h:2178 Form/form_us.xml.h:2216 msgid "Divorced" -msgstr "" +msgstr "Boşanmış" #: Form/form_us.xml.h:2067 msgid "Can Read" -msgstr "" +msgstr "Okuyabiliyor" #: Form/form_us.xml.h:2068 msgid "Can't Read" -msgstr "" +msgstr "Okuyamıyor" #: Form/form_us.xml.h:2069 msgid "Can Write" -msgstr "" +msgstr "Yazabiliyor" #: Form/form_us.xml.h:2070 msgid "Can't Write" -msgstr "" +msgstr "Yazamıyor" #: Form/form_us.xml.h:2083 Form/form_us.xml.h:2120 Form/form_us.xml.h:2158 #: Form/form_us.xml.h:2196 msgid "Do you own your home or farm" -msgstr "" +msgstr "Eviniz veya çiftliğiniz size mi ait" #: Form/form_us.xml.h:2085 Form/form_us.xml.h:2122 Form/form_us.xml.h:2160 #: Form/form_us.xml.h:2198 TimelinePedigreeView/TimelinePedigreeView.gpr.py:34 msgid "Ancestry" -msgstr "" +msgstr "Soy" #: Form/form_us.xml.h:2086 Form/form_us.xml.h:2123 Form/form_us.xml.h:2161 #: Form/form_us.xml.h:2199 msgid "Father's birthplace" -msgstr "" +msgstr "Babanın doğum yeri" #: Form/form_us.xml.h:2087 Form/form_us.xml.h:2124 Form/form_us.xml.h:2162 #: Form/form_us.xml.h:2200 msgid "Mother's birthplace" -msgstr "" +msgstr "Annenin doğum yeri" #: Form/form_us.xml.h:2088 Form/form_us.xml.h:2125 Form/form_us.xml.h:2163 #: Form/form_us.xml.h:2201 msgid "Extent of Education" -msgstr "" +msgstr "Eğitim Düzeyi" #: Form/form_us.xml.h:2089 Form/form_us.xml.h:2126 Form/form_us.xml.h:2164 #: Form/form_us.xml.h:2202 msgid "Graduate of" -msgstr "" +msgstr "Mezun Olduğu Okul" #: Form/form_us.xml.h:2094 Form/form_us.xml.h:2132 Form/form_us.xml.h:2170 #: Form/form_us.xml.h:2208 msgid "Maiden name of wife" -msgstr "" +msgstr "Eşin kızlık soyadı" #: Form/form_us.xml.h:2096 Form/form_us.xml.h:2134 Form/form_us.xml.h:2172 #: Form/form_us.xml.h:2210 msgid "Church affiliation" -msgstr "" +msgstr "Bağlı olduğu kilise" #: Form/form_us.xml.h:2109 Form/form_us.xml.h:2147 Form/form_us.xml.h:2185 #: Form/form_us.xml.h:2223 msgid "If Foreign Born, are you Naturalized" -msgstr "" +msgstr "Yabancı Doğumluysa, Vatandaşlığa Kabul Edildi mi" #: Form/form_us.xml.h:2111 Form/form_us.xml.h:2149 Form/form_us.xml.h:2187 #: Form/form_us.xml.h:2225 msgid "Years in S.D." -msgstr "" +msgstr "Güney Dakota'da Geçirilen Yıllar." #: Form/form_us.xml.h:2131 Form/form_us.xml.h:2169 Form/form_us.xml.h:2207 msgid "Division" -msgstr "" +msgstr "Bölge" #: Form/form_us.xml.h:2228 msgid "Male under 21" -msgstr "" +msgstr "21 yaş altı erkek" #: Form/form_us.xml.h:2229 msgid "Male over 21" -msgstr "" +msgstr "21 yaş üstü erkek" #: Form/form_us.xml.h:2230 msgid "Female under 21" -msgstr "" +msgstr "21 yaş altı kadın" #: Form/form_us.xml.h:2231 msgid "Female over 21" -msgstr "" +msgstr "21 yaş üstü kadın" #: Form/form_us.xml.h:2234 Form/form_us.xml.h:2246 msgid "Town/City" -msgstr "" +msgstr "Kasaba/Şehir" #: Form/form_us.xml.h:2236 Form/form_us.xml.h:2248 Form/form_us.xml.h:2260 msgid "Heads of Families" -msgstr "" +msgstr "Aile Reisleri" #: Form/form_us.xml.h:2237 Form/form_us.xml.h:2249 Form/form_us.xml.h:2261 #: Form/form_us.xml.h:2273 Form/form_us.xml.h:2302 msgid "White males" -msgstr "" +msgstr "Beyaz erkekler" #: Form/form_us.xml.h:2238 Form/form_us.xml.h:2250 Form/form_us.xml.h:2262 #: Form/form_us.xml.h:2274 Form/form_us.xml.h:2303 msgid "White females" -msgstr "" +msgstr "Beyaz kadınlar" #: Form/form_us.xml.h:2239 Form/form_us.xml.h:2251 Form/form_us.xml.h:2263 #: Form/form_us.xml.h:2275 Form/form_us.xml.h:2304 msgid "Colored males" -msgstr "" +msgstr "Renkli erkekler" #: Form/form_us.xml.h:2240 Form/form_us.xml.h:2252 Form/form_us.xml.h:2264 #: Form/form_us.xml.h:2276 Form/form_us.xml.h:2305 msgid "Colored females" -msgstr "" +msgstr "Renkli kadınlar" #: Form/form_us.xml.h:2241 Form/form_us.xml.h:2253 Form/form_us.xml.h:2265 msgid "Deaf & Dumb" -msgstr "" +msgstr "Sağır ve Dilsiz" #: Form/form_us.xml.h:2244 Form/form_us.xml.h:2256 msgid "Foreign Birth" -msgstr "" +msgstr "Yabancı Doğumlu" #: Form/form_us.xml.h:2258 msgid "Inhabitants of" -msgstr "" +msgstr "Sakinleri" #: Form/form_us.xml.h:2277 Form/form_us.xml.h:2306 msgid "Nativity - United States" -msgstr "" +msgstr "Doğum Yeri - Amerika Birleşik Devletleri" #: Form/form_us.xml.h:2278 Form/form_us.xml.h:2307 msgid "Nativity - Germany" -msgstr "" +msgstr "Doğum Yeri - Almanya" #: Form/form_us.xml.h:2279 Form/form_us.xml.h:2308 msgid "Nativity - Great Britain" -msgstr "" +msgstr "Doğum Yeri - Büyük Britanya" #: Form/form_us.xml.h:2280 Form/form_us.xml.h:2309 msgid "Nativity - Ireland" -msgstr "" +msgstr "Doğum Yeri - İrlanda" #: Form/form_us.xml.h:2281 Form/form_us.xml.h:2310 msgid "Nativity - France" -msgstr "" +msgstr "Doğum Yeri - Fransa" #: Form/form_us.xml.h:2282 Form/form_us.xml.h:2311 msgid "Nativity - British America" -msgstr "" +msgstr "Doğum Yeri - Britanya Amerikası" #: Form/form_us.xml.h:2283 Form/form_us.xml.h:2312 msgid "Nativity - Scandinavian" -msgstr "" +msgstr "Doğum Yeri - İskandinavya" #: Form/form_us.xml.h:2284 Form/form_us.xml.h:2313 msgid "Nativity - Holland" -msgstr "" +msgstr "Doğum Yeri - Hollanda" #: Form/form_us.xml.h:2285 Form/form_us.xml.h:2314 msgid "Nativity - All other countries" -msgstr "" +msgstr "Doğum Yeri - Diğer tüm ülkeler" #: Form/form_us.xml.h:2287 Form/form_us.xml.h:2316 msgid "Residing in the" -msgstr "" +msgstr "İkamet eden" #: Form/form_us.xml.h:2292 Form/form_us.xml.h:2321 msgid "Co." -msgstr "" +msgstr "İlçe" #: Form/form_us.xml.h:2293 Form/form_us.xml.h:2322 msgid "Reg't." -msgstr "" +msgstr "Alay." #: Form/form_us.xml.h:2294 Form/form_us.xml.h:2323 msgid "State or Vessel" -msgstr "" +msgstr "Eyalet veya Gemi" #: Form/form_us.xml.h:2337 msgid "Parents place of birth" -msgstr "" +msgstr "Ebeveynlerin doğum yeri" #: Form/form_us.xml.h:2340 msgid "Owned/Rented" -msgstr "" +msgstr "Mülk Sahibi/Kiracı" #: Form/form_us.xml.h:2341 msgid "Free/Mortgaged" -msgstr "" +msgstr "Borçsuz/İpotekli" #: Form/form_us.xml.h:2342 msgid "Farm/House" -msgstr "" +msgstr "Çiftlik/Ev" #: Form/form_us.xml.h:2349 msgid "Twin, triplet or other" -msgstr "" +msgstr "İkiz, üçüz veya diğer" #: Form/form_us.xml.h:2350 msgid "No. in order of birth" -msgstr "" +msgstr "Doğum sırasındaki numarası" #: Form/form_us.xml.h:2351 msgid "Legitimate" -msgstr "" +msgstr "Meşru" #: Form/form_us.xml.h:2369 Form/form_us.xml.h:2390 Form/form_us.xml.h:2483 #: Form/form_us.xml.h:2592 msgid "Place of Record" -msgstr "" +msgstr "Kayıt Yeri" #: Form/form_us.xml.h:2370 Form/form_us.xml.h:2391 Form/form_us.xml.h:2484 #: Form/form_us.xml.h:2593 @@ -14834,190 +14896,195 @@ msgstr "Kitap/Cilt" #: Form/form_us.xml.h:2377 Form/form_us.xml.h:2493 msgid "Race/Color" -msgstr "" +msgstr "Irk/Renk" #: Form/form_us.xml.h:2380 msgid "" "STOP! If you want this record to be attached to the Parents move to Father." -msgstr "" +msgstr "DUR! Bu kaydın Ebeveynlere eklenmesini istiyorsanız Babaya gidin." #: Form/form_us.xml.h:2381 msgid "Mother (Maiden Name)" -msgstr "" +msgstr "Anne (Kızlık Soyadı)" #: Form/form_us.xml.h:2382 msgid "Residence of Parents" -msgstr "" +msgstr "Ebeveynlerin İkamet Yeri" #: Form/form_us.xml.h:2383 msgid "By Whom Reported" -msgstr "" +msgstr "Bildirimi Yapan Kişi" #: Form/form_us.xml.h:2384 Form/form_us.xml.h:2501 msgid "Date Recorded" -msgstr "" +msgstr "Kayıt Tarihi" #: Form/form_us.xml.h:2393 msgid "License No." -msgstr "" +msgstr "Lisans No." #: Form/form_us.xml.h:2395 msgid "Date of Marriage" -msgstr "" +msgstr "Evlilik Tarihi" #: Form/form_us.xml.h:2396 msgid "Married by" -msgstr "" +msgstr "Evlendiren Kişi" #: Form/form_us.xml.h:2398 msgid "Place of Marriage" -msgstr "" +msgstr "Evlilik Yeri" #: Form/form_us.xml.h:2400 msgid "Consent given by" -msgstr "" +msgstr "Onay veren kişi" #: Form/form_us.xml.h:2403 msgid "Bond/Licence" -msgstr "" +msgstr "Teminat/Lisans" #: Form/form_us.xml.h:2434 msgid "Length of Residence" -msgstr "" +msgstr "İkamet Süresi" #: Form/form_us.xml.h:2435 msgid "Served in U.S. Military" -msgstr "" +msgstr "ABD Ordusunda Hizmet Etti" #: Form/form_us.xml.h:2440 msgid "Birth date of deceased" -msgstr "" +msgstr "Ölenin doğum tarihi" #: Form/form_us.xml.h:2443 msgid "Industry or Business" -msgstr "" +msgstr "Sektör veya İşletme" #: Form/form_us.xml.h:2444 msgid "Name of employer" -msgstr "" +msgstr "İşverenin adı" #: Form/form_us.xml.h:2445 msgid "Date last worked" -msgstr "" +msgstr "Son çalışma tarihi" #: Form/form_us.xml.h:2446 msgid "Years in this occupation" -msgstr "" +msgstr "Bu meslekte geçirilen yıllar" #: Form/form_us.xml.h:2447 msgid "Education Level" -msgstr "" +msgstr "Eğitim Düzeyi" #: Form/form_us.xml.h:2453 msgid "Social Security No." -msgstr "" +msgstr "Sosyal Güvenlik No." #: Form/form_us.xml.h:2457 msgid "Place of Burial" -msgstr "" +msgstr "Defin Yeri" #: Form/form_us.xml.h:2458 msgid "Date of Burial" -msgstr "" +msgstr "Defin Tarihi" #: Form/form_us.xml.h:2478 msgid "Residence Place" -msgstr "" +msgstr "İkametgâh Yeri" #: Form/form_us.xml.h:2480 msgid "Burial Date" -msgstr "" +msgstr "Defin Tarihi" #: Form/form_us.xml.h:2481 msgid "Burial Place" -msgstr "" +msgstr "Defin Yeri" #: Form/form_us.xml.h:2503 msgid "Congressional District" -msgstr "" +msgstr "Kongre Bölgesi" #: Form/form_us.xml.h:2504 msgid "in the Counties of" -msgstr "" +msgstr "İlçelerinde" #: Form/form_us.xml.h:2506 msgid "in the month of" -msgstr "" +msgstr "Ayında" #: Form/form_us.xml.h:2510 msgid "Age 1 July 1863" -msgstr "" +msgstr "1 Temmuz 1863 tarihindeki yaşı" #: Form/form_us.xml.h:2511 msgid "White or Colored" -msgstr "" +msgstr "Beyaz veya Renkli" #: Form/form_us.xml.h:2514 msgid "Former Military Service" -msgstr "" +msgstr "Önceki Askerlik Hizmeti" #: Form/form_us.xml.h:2516 msgid "Station Headquarters" -msgstr "" +msgstr "Karargâh" #: Form/form_us.xml.h:2517 msgid "Congr. Dist. of" -msgstr "" +msgstr "Kongre Bölgesi" #: Form/form_us.xml.h:2519 msgid "Card (A, B, or C)" -msgstr "" +msgstr "Kart (A, B veya C)" #: Form/form_us.xml.h:2521 msgid "Serial No." -msgstr "" +msgstr "Seri No." #: Form/form_us.xml.h:2522 msgid "Registration No." -msgstr "" +msgstr "Kayıt No." #: Form/form_us.xml.h:2523 msgid "Order No." -msgstr "" +msgstr "Sıra No." #: Form/form_us.xml.h:2525 msgid "Age in yrs." -msgstr "" +msgstr "Yaş, yıl olarak." #: Form/form_us.xml.h:2526 msgid "Home Address" -msgstr "" +msgstr "Ev Adresi" #: Form/form_us.xml.h:2530 msgid "Natural-born, Naturalized Citizen, Alien, Noncitizen or citizen Indian" msgstr "" +"Doğuştan Yerli, Vatandaşlığa Kabul Edilmiş Vatandaş, Yabancı, Vatandaş " +"Olmayan veya Hintli Vatandaş" #: Form/form_us.xml.h:2531 msgid "If not a citizen, of what nation are you a citizen or subject?" -msgstr "" +msgstr "Vatandaş değilseniz, hangi ülkenin vatandaşı veya tebaasısınız?" #: Form/form_us.xml.h:2533 Form/form_us.xml.h:2575 msgid "Employer's Name & address" -msgstr "" +msgstr "İşverenin Adı ve Adresi" #: Form/form_us.xml.h:2534 msgid "Nearest Relative" -msgstr "" +msgstr "En Yakın Akraba" #: Form/form_us.xml.h:2535 msgid "Dependants & Address" -msgstr "" +msgstr "Bağımlı Kişiler ve Adresleri" #: Form/form_us.xml.h:2536 msgid "" "Have you a father, mother, wife, child under 12, or a sister or brother " "under 12, solely dependent on your support (specify which)?" msgstr "" +"Yalnızca sizin desteğinize bağımlı olan bir babanız, anneniz, eşiniz, 12 " +"yaşından küçük çocuğunuz veya 12 yaşından küçük kız ya da erkek kardeşiniz " +"var mı (hangisi olduğunu belirtin)?" #: Form/form_us.xml.h:2538 msgid "What military service have you had? (Branch, Years, Nation or State)" @@ -15781,14 +15848,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "GrampsChat" msgid "Gramps Assistant" -msgstr "Gramps Sohbeti" +msgstr "Büyükbaba Asistanı" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "Gramps aile ağacınızı sorgulamak için yapay zekâ asistanı" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15798,27 +15863,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Siz, kullanıcının Gramps veritabanına erişimi olan yardımsever bir soy ağacı " +"asistanısınız. Kişiler, aileler, olaylar ve ilişkiler hakkında soruları " +"yanıtlayın. Veritabanından bilgiye ihtiyacınız olduğunda, sağlanan araçları " +"kullanın; asla kod yazmayın, sonuçları simüle etmeyin veya veri uydurmayın. " +"İstenen bilgi için bir araç yoksa, bunu açıkça belirtin." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Ekstra stil ayarları:" +msgstr "Gramps Asistanı ayarları" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Sohbeti ve bağlamı temizle" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 msgid "Gramps Assistant:" -msgstr "" +msgstr "Gramps Asistanı:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Gramps programı veya size özel Gramps aile ağacınız hakkında bana " +"istediğiniz her şeyi sorun. Yapay zekâyı yapılandırmak için ⚙ düğmesini " +"kullanın.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15826,10 +15897,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Model yapılandırılmamış. Lütfen sohbet etmeye başlamadan önce bir arka uç ve " +"model seçmek için Ayarlar düğmesine tıklayın.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Düşünüyor..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15838,89 +15912,96 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"API anahtarı hatası: {var} ortam değişkeni ayarlanmamış veya geçersiz. " +"Gramps'i başlatmadan önce ayarlayın:\n" +"dışa aktar {var}=anahtarınızı-buraya-girin" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"API anahtarı hatası: Bu sağlayıcı bir API anahtarı gerektiriyor. Ayarları " +"açın ve API anahtarınızın ortam değişkeni adını girin (örneğin, " +"OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Bitti!\n" +msgstr "Bitti.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Sistem İstemi:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Basitleştirme araçları (daha küçük/yerel modeller için önerilir)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Etkinleştirildiğinde, modele yalnızca sorunuzla ilgili araçlar gönderilir. " +"Bu, daha küçük yerel modellerde performansı artırır." #: GrampsAssistant/grampsassistant.py:1230 msgid "Use Local Model" -msgstr "" +msgstr "Yerel Modeli Kullanın" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"Yerel OpenAI uyumlu sunucunun URL adresi. Ollama: http://localhost:11434 LM " +"Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "Model adı (LM Studio / llama.cpp için boş bırakın)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Yerel sunucudan talep edilecek model. Ollama için gereklidir (örneğin " +"llama3.1). LM Studio veya llama.cpp için boş bırakın, bunlar yüklenen modeli " +"kullanır." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Modern" +msgstr "Model:" #: GrampsAssistant/grampsassistant.py:1273 msgid "Use Foundational Model" -msgstr "" +msgstr "Temel Modeli Kullanın" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "örneğin OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "API anahtarınızı içeren ortam değişkeninin adı." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "API anahtar ortam değişkeni:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Arka uç:" #: GrampsAssistant/grampsassistant.py:1334 msgid "Base URL:" -msgstr "" +msgstr "Temel URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Source type" msgid "Model name:" -msgstr "Kaynak türü" +msgstr "Model adı:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -16652,168 +16733,178 @@ msgstr "Grafik Görünümü Hakkında" #: QuiltView/QuiltView.py:1601 #, python-format msgid "Adding Tags to person (%s)" -msgstr "" +msgstr "Kişiye (%s) etiket ekleme" #: GraphView/graphview.py:4553 GraphView/graphview.py:4605 #: QuiltView/QuiltView.py:1606 #, python-format msgid "Adding Tags to family (%s)" -msgstr "" +msgstr "Aileye etiket ekleme (%s)" #: GraphView/graphview.py:4679 #, python-format msgid "Deleting the person [%s] will remove it from the database." -msgstr "" +msgstr "[%s] kişisini silmek, onu veritabanından kaldıracaktır." #: GraphView/graphview.py:4708 #, python-format msgid "Delete family [%s]?" -msgstr "" +msgstr "[%s] ailesi silinsin mi?" #: GraphView/graphview.py:4709 msgid "Deleting the family will remove it from the database." -msgstr "" +msgstr "Aileyi silmek, onu veritabanından kaldıracaktır." #: GraphView/graphview.py:4720 #, python-format msgid "Delete Family [%s]" -msgstr "" +msgstr "Aileyi Sil [%s]" #: GraphView/search_widget.py:82 msgid "Persons from current graph" -msgstr "" +msgstr "Mevcut grafikteki kişiler" #: GraphView/search_widget.py:83 msgid "Other persons from database" -msgstr "" +msgstr "Veritabanındaki diğer kişiler" #: GraphView/search_widget.py:235 msgid "No persons found..." -msgstr "" +msgstr "Kişi bulunamadı..." #: GraphView/search_widget.py:398 msgid "" "Search people in the current visible graph and database.\n" "Use to make search entry active." msgstr "" +"Mevcut görünür grafikte ve veritabanında kişileri arayın.\n" +"Arama girişini etkinleştirmek için tuşunu kullanın." #: GraphView/search_widget.py:400 PluginManager/PluginManager.py:169 #: SearchGramplet/SearchGramplet.py:391 msgid "Search..." -msgstr "" +msgstr "Ara..." #: GraphView/search_widget.py:592 #, python-format msgid "%s:" -msgstr "" +msgstr "%s:" #: GraphView/search_widget.py:783 msgid "Partners" -msgstr "" +msgstr "Partnerler" #: HasTagSubstr/hastagsubstr.gpr.py:25 HasTagSubstr/hastagsubstr.py:93 msgid "People with a tag containing " -msgstr "" +msgstr " içeren etikete sahip kişiler" #: HasTagSubstr/hastagsubstr.gpr.py:26 HasTagSubstr/hastagsubstr.py:94 msgid "Matches people with a tag whose name contains the given substring" msgstr "" +"Verilen alt dizeyi içeren ada sahip bir etiketi olan kişilerle eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:40 HasTagSubstr/hastagsubstr.py:102 msgid "Families with a tag containing " -msgstr "" +msgstr " içeren etikete sahip aileler" #: HasTagSubstr/hastagsubstr.gpr.py:42 HasTagSubstr/hastagsubstr.py:104 msgid "Matches families with a tag whose name contains the given substring" msgstr "" +"Adında verilen alt dizeyi içeren bir etikete sahip eşleşmeleri ailelerle " +"eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:57 HasTagSubstr/hastagsubstr.py:113 msgid "Events with a tag containing " -msgstr "" +msgstr " etiketi içeren etkinlikler" #: HasTagSubstr/hastagsubstr.gpr.py:59 HasTagSubstr/hastagsubstr.py:115 msgid "Matches events with a tag whose name contains the given substring" msgstr "" +"Adında belirtilen alt dizeyi içeren etikete sahip etkinlikleri eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:74 HasTagSubstr/hastagsubstr.py:124 msgid "Places with a tag containing " -msgstr "" +msgstr " etiketi içeren yerler" #: HasTagSubstr/hastagsubstr.gpr.py:76 HasTagSubstr/hastagsubstr.py:126 msgid "Matches places with a tag whose name contains the given substring" -msgstr "" +msgstr "Adında verilen alt dizeyi içeren etikete sahip yerlerle eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:91 HasTagSubstr/hastagsubstr.py:135 msgid "Sources with a tag containing " -msgstr "" +msgstr " etiketi içeren kaynaklar" #: HasTagSubstr/hastagsubstr.gpr.py:93 HasTagSubstr/hastagsubstr.py:137 msgid "Matches sources with a tag whose name contains the given substring" -msgstr "" +msgstr "Adında belirtilen alt dizeyi içeren etikete sahip kaynakları eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:108 HasTagSubstr/hastagsubstr.py:146 msgid "Citations with a tag containing " -msgstr "" +msgstr " etiketi içeren alıntılar" #: HasTagSubstr/hastagsubstr.gpr.py:110 HasTagSubstr/hastagsubstr.py:148 msgid "Matches citations with a tag whose name contains the given substring" -msgstr "" +msgstr "Alıntıları, adında belirtilen alt dizeyi içeren bir etiketle eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:125 HasTagSubstr/hastagsubstr.py:157 msgid "Repositories with a tag containing " -msgstr "" +msgstr " içeren etikete sahip depolar" #: HasTagSubstr/hastagsubstr.gpr.py:127 HasTagSubstr/hastagsubstr.py:159 msgid "Matches repositories with a tag whose name contains the given substring" -msgstr "" +msgstr "Adında belirtilen alt dizeyi içeren etikete sahip depoları eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:142 HasTagSubstr/hastagsubstr.py:168 msgid "Media objects with a tag containing " -msgstr "" +msgstr " etiketi içeren medya nesneleri" #: HasTagSubstr/hastagsubstr.gpr.py:144 HasTagSubstr/hastagsubstr.py:170 msgid "" "Matches media objects with a tag whose name contains the given substring" msgstr "" +"Adında belirtilen alt dizeyi içeren etikete sahip medya nesnelerini " +"eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:159 HasTagSubstr/hastagsubstr.py:179 msgid "Notes with a tag containing " -msgstr "" +msgstr " etiketi içeren notlar" #: HasTagSubstr/hastagsubstr.gpr.py:161 HasTagSubstr/hastagsubstr.py:181 msgid "Matches notes with a tag whose name contains the given substring" -msgstr "" +msgstr "Adında verilen alt dizeyi içeren bir etikete sahip notları eşleştirir" #: HeadlineNewsGramplet/HeadlineNewsGramplet.gpr.py:4 #: HeadlineNewsGramplet/HeadlineNewsGramplet.gpr.py:11 msgid "Headline News" -msgstr "" +msgstr "Manşet Haberler" #: HeadlineNewsGramplet/HeadlineNewsGramplet.gpr.py:5 msgid "Gramplet that shows the latest Gramps news" -msgstr "" +msgstr "Gramplet, Gramps ile ilgili en son haberleri gösteriyor" #: HeadlineNewsGramplet/HeadlineNewsGramplet.py:113 msgid "Read Gramps headline news" -msgstr "" +msgstr "Gramps manşet haberlerini oku" #: Heatmap/heatmap.gpr.py:26 msgid "Create a heatmap web report." -msgstr "" +msgstr "Bir ısı haritası web raporu oluşturun." #: Heatmap/heatmap.py:85 msgid "Map tiles" -msgstr "" +msgstr "Harita döşemeleri" #: Heatmap/heatmap.py:90 msgid "" "Set the size of the heatmap points.\n" "Default: 15" msgstr "" +"Isı haritası noktalarının boyutunu ayarlayın.\n" +"Varsayılan: 15" #: Heatmap/heatmap.py:93 msgid "File path" -msgstr "" +msgstr "Dosya yolu" #: Heatmap/heatmap.py:98 msgid "" @@ -16822,226 +16913,241 @@ msgid "" "has to start and end with an alphanumeric character.The file extention " "'.html' is added by the report." msgstr "" +"Dosya adı için yalnızca A-Z, a-z İngilizce harfler ve 0-9 sayıları ile " +"boşluk, alt çizgi ve tire karakterlerine izin verilir. Dosya adı ayrıca " +"alfasayısal bir karakterle başlamalı ve bitmelidir. Dosya uzantısı '.html' " +"rapor tarafından eklenir." #: Heatmap/heatmap.py:103 msgid "File name" -msgstr "" +msgstr "Dosya adı" #: Heatmap/heatmap.py:117 msgid "Enable custom start position" -msgstr "" +msgstr "Özel başlangıç konumunu etkinleştirin" #: Heatmap/heatmap.py:119 msgid "" "Enabling will force the map open at your custom start position and zoom." msgstr "" +"Etkinleştirildiğinde, harita özel başlangıç konumunuzda ve yakınlaştırma " +"seviyenizde açılacaktır." #: Heatmap/heatmap.py:124 msgid "Start latitude" -msgstr "" +msgstr "Başlangıç enlemi" #: Heatmap/heatmap.py:126 msgid "" "Set custom start position latitude\n" "Default: 50.0" msgstr "" +"Özel başlangıç pozisyonu enlemini ayarlayın\n" +"Varsayılan: 50.0" #: Heatmap/heatmap.py:129 msgid "Start longitude" -msgstr "" +msgstr "Başlangıç boylamı" #: Heatmap/heatmap.py:131 msgid "" "Set custom start position longitude\n" "Default: 10.0" msgstr "" +"Özel başlangıç pozisyonu boylamını ayarlayın\n" +"Varsayılan: 10.0" #: Heatmap/heatmap.py:134 msgid "Start zoom" -msgstr "" +msgstr "Yakınlaştırmayı başlat" #: Heatmap/heatmap.py:136 msgid "" "Set the value for the starting zoom\n" "Default: 5" msgstr "" +"Başlangıç yakınlaştırma değeri için ayar yapın\n" +"Varsayılan: 5" #: Heatmap/heatmap.py:209 msgid "Path does not exist." -msgstr "" +msgstr "Yol mevcut değil." #: Heatmap/heatmap.py:210 msgid "Invalid filename." -msgstr "" +msgstr "Geçersiz dosya adı." #: Heatmap/utils.py:55 msgid "Stamen Terrain" -msgstr "" +msgstr "Stamen Arazisi" #: Heatmap/utils.py:58 msgid "Stamen Terrain (Background only)" -msgstr "" +msgstr "Stamen Arazisi (Sadece arka plan)" #: Heatmap/utils.py:61 msgid "Stamen Toner" -msgstr "" +msgstr "Stamen Toner" #: Heatmap/utils.py:64 msgid "Stamen Watercolor" -msgstr "" +msgstr "Stamen Suluboya" #: Heatmap/utils.py:67 msgid "CartoDB Positron" -msgstr "" +msgstr "CartoDB Pozitron" #: Heatmap/utils.py:70 msgid "CartoDB DarkMatter" -msgstr "" +msgstr "CartoDB Karanlık Madde" #: HistContext/HistContext.gpr.py:4 HistContext/HistContext.gpr.py:14 msgid "Historical Context" -msgstr "" +msgstr "Tarihsel Bağlam" #: HistContext/HistContext.gpr.py:5 msgid "Lists relevant historical events during the lifetime of a Person" msgstr "" +"Bir kişinin yaşamı boyunca meydana gelen ilgili tarihsel olayları listeler" #: HistContext/HistContext.py:123 msgid "Rows starting with this in the text column will be hidden " -msgstr "" +msgstr "Metin sütununda bununla başlayan satırlar gizlenecektir " #: HistContext/HistContext.py:126 msgid "Use filter " -msgstr "" +msgstr "Filtreyi kullan " #: HistContext/HistContext.py:129 msgid "Show outside life span " -msgstr "" +msgstr "Yaşam süresi dışında göster " #: HistContext/HistContext.py:132 msgid "Use full dates" -msgstr "" +msgstr "Tam tarihleri kullanın" #: HistContext/HistContext.py:135 msgid "Foreground color items in lifespan" -msgstr "" +msgstr "Yaşam süresi boyunca ön plan renk öğeleri" #: HistContext/HistContext.py:138 msgid "Background color items in lifespan" -msgstr "" +msgstr "Yaşam süresindeki arka plan rengi öğeleri" #: HistContext/HistContext.py:141 msgid "Foreground color items outside lifespan" -msgstr "" +msgstr "Yaşam süresi dışındaki ön plan renkli öğeler" #: HistContext/HistContext.py:144 msgid "Background color items outside lifespan" -msgstr "" +msgstr "Yaşam süresi dışındaki arka plan rengi öğeleri" #: HistContext/HistContext.py:149 msgid "Select from files" -msgstr "" +msgstr "Dosyalardan seçin" #: HistContext/HistContext.py:287 msgid "Invalid date " -msgstr "" +msgstr "Geçersiz tarih " #: HistContext/HistContext.py:288 msgid " in line: " -msgstr "" +msgstr " satırda: " #: HistContext/HistContext.py:289 HistContext/HistContext.py:333 #: HistContext/HistContext.py:420 msgid "Error:" -msgstr "" +msgstr "Hata:" #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" -msgstr "" +msgstr ": satır, noktalı virgülle ayrılmış dört bölüm içermiyor : \"" #: HistContext/HistContext.py:419 msgid "Cannot open URL: " -msgstr "" +msgstr "URL açılamıyor: " #: HistContext/HistContext.py:427 msgid "Double click row to follow link" -msgstr "" +msgstr "Bağlantıyı takip etmek için satıra çift tıklayın" #: HouseTimelineGramplet/housetimeline.gpr.py:4 #: HouseTimelineGramplet/housetimeline.gpr.py:15 msgid "House Timeline" -msgstr "" +msgstr "Evin Zaman Çizelgesi" #: HouseTimelineGramplet/housetimeline.gpr.py:5 msgid "Lists the Residents of an Address by Timeline" -msgstr "" +msgstr "Bir Adresin Sakinlerini Zaman Çizelgesine Göre Listeler" #: HouseTimelineGramplet/housetimeline.py:51 #: HouseTimelineGramplet/housetimeline.py:58 msgid "House Icon Style" -msgstr "" +msgstr "Ev Simgesi Stili" #: HouseTimelineGramplet/housetimeline.py:61 msgid "Unicode" -msgstr "" +msgstr "Unicode" #: HouseTimelineGramplet/housetimeline.py:90 msgid "" "There are no individuals with Address data. Please add Address data to " "people." msgstr "" +"Adres bilgisi olan kişi bulunmamaktadır. Lütfen kişilere adres bilgisi " +"ekleyin." #: HouseTimelineGramplet/housetimeline.py:129 msgid "Time In Family" -msgstr "" +msgstr "Aile İçinde Geçirilen Süre" #: HouseTimelineGramplet/housetimeline.py:130 msgid "First Resident" -msgstr "" +msgstr "İlk İkamet" #: HouseTimelineGramplet/housetimeline.py:131 msgid "Last Resident" -msgstr "" +msgstr "Son İkamet" #: HouseTimelineGramplet/housetimeline.py:134 msgid "Total Known Residents" -msgstr "" +msgstr "Toplam Bilinen İkametler" #: HtmlView/htmlview.gpr.py:31 msgid "Html View" -msgstr "" +msgstr "Html Görünümü" #: HtmlView/htmlview.gpr.py:32 msgid "A view showing html pages embedded in Gramps" -msgstr "" +msgstr "Gramps içinde gömülü html sayfalarını gösteren bir görünüm" #: HtmlView/htmlview.gpr.py:40 msgid "Web" -msgstr "" +msgstr "Web" #: HtmlView/htmlview.py:328 msgid "HtmlView" -msgstr "" +msgstr "Html Görünümü" #: HtmlView/htmlview.py:495 msgid "Go to the previous page in the history" -msgstr "" +msgstr "Geçmişteki önceki sayfaya git" #: HtmlView/htmlview.py:503 msgid "Go to the next page in the history" -msgstr "" +msgstr "Geçmişteki bir sonraki sayfaya git" #: HtmlView/htmlview.py:508 msgid "_Refresh" -msgstr "" +msgstr "_Yenile" #: HtmlView/htmlview.py:511 msgid "Stop and reload the page." -msgstr "" +msgstr "Durdur ve sayfayı yeniden yükle." #: HtmlView/htmlview.py:555 msgid "Start page for the Html View" -msgstr "" +msgstr "Html Görünümü için başlangıç sayfası" #: HtmlView/htmlview.py:556 msgid "" @@ -17050,63 +17156,69 @@ msgid "" "
\n" "For example: " msgstr "" +"Sayfanın üst kısmına bir web sayfası adresi yazın ve bu sayfada bir web " +"sayfası yüklemek için yürüt düğmesine basın.\n" +"
\n" +"Örneğin: " #: HtreePedigreeView/HtreePedigreeView.gpr.py:34 #: HtreePedigreeView/HtreePedigreeView.gpr.py:47 msgid "H-Tree Pedigree" -msgstr "" +msgstr "H-Ağaç Soyağacı" #: HtreePedigreeView/HtreePedigreeView.gpr.py:37 msgid "" "The view shows a space-efficient pedigree with ancestors of the selected " "person" msgstr "" +"Bu görünüm, seçilen kişinin atalarını gösteren, yerden tasarruf sağlayan bir " +"soy ağacını sergiliyor" #: HtreePedigreeView/HtreePedigreeView.py:93 #: TimelinePedigreeView/TimelinePedigreeView.py:82 msgid "short for born|b." -msgstr "" +msgstr "doğumun kısaltması|d." #: HtreePedigreeView/HtreePedigreeView.py:94 #: TimelinePedigreeView/TimelinePedigreeView.py:83 msgid "short for died|d." -msgstr "" +msgstr "ölümün kısaltması|ö." #: HtreePedigreeView/HtreePedigreeView.py:95 #: TimelinePedigreeView/TimelinePedigreeView.py:84 msgid "short for baptized|bap." -msgstr "" +msgstr "vaftizin kısaltması|vaf." #: HtreePedigreeView/HtreePedigreeView.py:96 #: TimelinePedigreeView/TimelinePedigreeView.py:85 msgid "short for christened|chr." -msgstr "" +msgstr "vaftiz edilmişin kısaltması|ved." #: HtreePedigreeView/HtreePedigreeView.py:97 #: TimelinePedigreeView/TimelinePedigreeView.py:86 msgid "short for buried|bur." -msgstr "" +msgstr "defnin kısaltması|def." #: HtreePedigreeView/HtreePedigreeView.py:98 #: TimelinePedigreeView/TimelinePedigreeView.py:87 msgid "short for cremated|crem." -msgstr "" +msgstr "yakmanın kısaltması|yakm." #: HtreePedigreeView/HtreePedigreeView.py:543 msgid "H-tree Pedigree View" -msgstr "" +msgstr "H-ağacı Soy Ağacı Görünümü" #: HtreePedigreeView/HtreePedigreeView.py:1692 msgid "About H-Tree" -msgstr "" +msgstr "H-Ağacı Hakkında" #: ImportGramplet/ImportGramplet.gpr.py:11 msgid "Gramplet for importing text" -msgstr "" +msgstr "Gramplet metin içe aktarma aracı" #: ImportGramplet/ImportGramplet.py:94 msgid "VCard import" -msgstr "" +msgstr "VCard içe aktarma" #: ImportGramplet/ImportGramplet.py:144 #, python-format @@ -17125,433 +17237,473 @@ msgid "" "Enter text to import and then click\n" "the Import button at bottom" msgstr "" +"İçe aktarmak istediğiniz metni girin ve \n" +"ardından alttaki İçe Aktar düğmesine tıklayın" #: ImportGramplet/ImportGramplet.py:198 msgid "_Import" -msgstr "" +msgstr "_İçe aktar" #: ImportGramplet/ImportGramplet.py:218 msgid "Import done" -msgstr "" +msgstr "İçe aktarma bitti" #: ImportGramplet/ImportGramplet.py:228 msgid "Importing Text..." -msgstr "" +msgstr "Metin içe aktarılıyor..." #: ImportGramplet/ImportGramplet.py:259 msgid "Can't determine type of import" -msgstr "" +msgstr "İçe aktarma türü belirlenemiyor" #: ImportMerge/importmerge.glade:8 msgid "" "A '*' indicates that the action was automarked, a '?' indicates a conflict " "between automarks." msgstr "" +"'*' işareti, bir işlemin otomatik olarak işaretlendiğini, '?' işareti ise " +"otomatik işaretlemeler arasında bir çakışma olduğunu gösterir." #: ImportMerge/importmerge.glade:39 msgid "Import file" -msgstr "" +msgstr "Dosyayı içe aktar" #: ImportMerge/importmerge.glade:124 msgid "Select the difference you wish to examine." -msgstr "" +msgstr "İncelemek istediğiniz farkı seçin." #: ImportMerge/importmerge.glade:180 msgid "Object Name/Description" -msgstr "" +msgstr "Nesne Adı/Açıklaması" #: ImportMerge/importmerge.glade:227 msgid "" "Data item details, showing the current treeitem, imported tree item, and " "result (when chosen)." msgstr "" +"Veri öğesi ayrıntıları; geçerli ağaç öğesini, içe aktarılan ağaç öğesini ve " +"sonucu (seçildiğinde) gösterir." #: ImportMerge/importmerge.glade:248 msgid "Object and item" -msgstr "" +msgstr "Nesne ve öğe" #: ImportMerge/importmerge.glade:293 ImportMerge/importmerge.py:116 msgid "" "Use buttons below to set the 'Action' for each difference. No changes will " "be made to your tree until you press 'Done' and confirm." msgstr "" +"Aşağıdaki düğmeleri kullanarak her bir fark için 'Eylem'i ayarlayın. " +"'Tamam' düğmesine basıp onaylamadığınız sürece ağacınızda hiçbir değişiklik " +"yapılmayacaktır." #: ImportMerge/importmerge.glade:334 ImportMerge/importmerge.py:110 msgid "Ignore" -msgstr "" +msgstr "Yok say" #: ImportMerge/importmerge.glade:338 msgid "This button will ignore the difference, no changes to your tree." -msgstr "" +msgstr "Bu düğme farkı yok sayacak, ağacınızda hiçbir değişiklik olmayacak." #: ImportMerge/importmerge.glade:349 msgid "Unmark" -msgstr "" +msgstr "İşaretsiz" #: ImportMerge/importmerge.glade:353 msgid "This button will add the imported data as a new object to your tree." msgstr "" +"Bu düğme, içe aktarılan verileri ağacınıza yeni bir nesne olarak " +"ekleyecektir." #: ImportMerge/importmerge.glade:368 msgid "" "This button will bring up a dialog allowing you to finalize your changes, or " "abandon them." msgstr "" +"Bu düğme, değişikliklerinizi kesinleştirmenize veya iptal etmenize olanak " +"tanıyan bir iletişim kutusu açacaktır." #: ImportMerge/importmerge.glade:392 msgid "Merge Original" -msgstr "" +msgstr "Orijinali Birleştir" #: ImportMerge/importmerge.glade:396 msgid "" "This button will merge the imported data into your tree with your tree data " "as a base." msgstr "" +"Bu düğme, içe aktarılan verileri, ağacınızdaki verileri temel alarak " +"ağacınızla birleştirecektir." #: ImportMerge/importmerge.glade:411 msgid "This button will replace your tree data with the imported data." -msgstr "" +msgstr "Bu düğme, ağaç verilerinizi içe aktarılan verilerle değiştirecektir." #: ImportMerge/importmerge.glade:422 msgid "Merge Import" -msgstr "" +msgstr "İçe Aktarmayı Birleştir" #: ImportMerge/importmerge.glade:426 msgid "" "This button will merge the imported data into your tree with the imported " "data as a base." msgstr "" +"Bu düğme, içe aktarılan verileri temel alarak bu verileri ağacınızla " +"birleştirecektir." #: ImportMerge/importmerge.glade:437 msgid "Edit Import" -msgstr "" +msgstr "İçe Aktarmayı Düzenle" #: ImportMerge/importmerge.glade:441 msgid "" "This button will edit the imported data. The data will still need to be " "merged, added or replaced." msgstr "" +"Bu düğme, içe aktarılan verileri düzenleyecektir. Verilerin yine de " +"birleştirilmesi, eklenmesi veya değiştirilmesi gerekecektir." #: ImportMerge/importmerge.glade:464 msgid "Show more details" -msgstr "" +msgstr "Daha fazla ayrıntı göster" #: ImportMerge/importmerge.glade:481 msgid "Automark Families" -msgstr "" +msgstr "Aileleri otomatik işaretle" #: ImportMerge/importmerge.glade:498 msgid "Automark Parent Families" -msgstr "" +msgstr "Ebeveyn Aileleri otomatik işaretle" #: ImportMerge/importmerge.gpr.py:31 ImportMerge/importmerge.py:329 #: ImportMerge/importmerge.py:708 msgid "Import and Merge tool" -msgstr "" +msgstr "İçe Aktarma ve Birleştirme aracı" #: ImportMerge/importmerge.gpr.py:33 msgid "" "Compares a Gramps XML database with the current one, and allows merging of " "the differences." msgstr "" +"Bir Gramps XML veritabanını mevcut veritabanıyla karşılaştırır ve " +"farklılıkların birleştirilmesine olanak tanır." #: ImportMerge/importmerge.py:81 msgid "Import and merge a Gramps XML" -msgstr "" +msgstr "Gramps XML dosyasını içe aktar ve birleştir" #: ImportMerge/importmerge.py:90 msgid "Missing" -msgstr "" +msgstr "Eksik" #: ImportMerge/importmerge.py:109 msgid "Delete original" -msgstr "" +msgstr "Orijinali sil" #: ImportMerge/importmerge.py:111 msgid "Add Import" -msgstr "" +msgstr "İçe Aktarma Ekle" #: ImportMerge/importmerge.py:112 msgid "Merge into original" -msgstr "" +msgstr "Orijinaline birleştir" #: ImportMerge/importmerge.py:113 msgid "Merge into import" -msgstr "" +msgstr "İçe aktarmaya birleştir" #: ImportMerge/importmerge.py:114 msgid "Replace with import" -msgstr "" +msgstr "İçe aktarma ile değiştir" #: ImportMerge/importmerge.py:115 msgid "Keep original" -msgstr "" +msgstr "Orijinalini koruyun" #: ImportMerge/importmerge.py:119 msgid "" "This item will be deleted from your tree. Any referenced items were also " "marked for deletion." msgstr "" +"Bu öğe ağacınızdan silinecektir. Referans verilen tüm öğeler de silinmek " +"üzere işaretlenmiştir." #: ImportMerge/importmerge.py:121 msgid "" "This item will not be changed in your tree. Any referenced items were also " "marked for Ignore." msgstr "" +"Bu öğe ağacınızda değiştirilmeyecektir. Referans verilen tüm öğeler de Yok " +"say olarak işaretlenmiştir." #: ImportMerge/importmerge.py:123 msgid "" "This item will be added to your tree. Any referenced items were also marked " "for adding." msgstr "" +"Bu öğe ağacınıza eklenecektir. Referans verilen tüm öğeler de eklenmek " +"üzere işaretlenmiştir." #: ImportMerge/importmerge.py:125 msgid "" "This item will be merged, saving data from your tree. Any referenced items " "were also marked for merging or adding." msgstr "" +"Bu öğe birleştirilecek ve ağacınızdaki veriler kaydedilecektir. Referans " +"verilen tüm öğeler de birleştirme veya ekleme için işaretlenmiştir." #: ImportMerge/importmerge.py:127 msgid "" "This item will be merged, using data from the import. Any referenced items " "were also marked for merging or adding." msgstr "" +"Bu öğe, içe aktarılan veriler kullanılarak birleştirilecektir. Referans " +"verilen tüm öğeler de birleştirme veya ekleme için işaretlenmiştir." #: ImportMerge/importmerge.py:129 msgid "" "The import data will entirely replace the data in your tree. Any referenced " "items were also marked for replacement or removal." msgstr "" +"İçe aktarılan veriler, ağacınızdaki verilerin tamamını değiştirecektir. " +"Referans verilen tüm öğeler de değiştirilmek veya kaldırılmak üzere " +"işaretlenmiştir." #: ImportMerge/importmerge.py:131 msgid "" "This item will not be changed in your tree. Any referenced items were also " "marked to keep." msgstr "" +"Bu öğe soy ağacınızda değiştirilmeyecektir. Referans verilen tüm öğeler de " +"saklanmak üzere işaretlenmiştir." #: ImportMerge/importmerge.py:339 msgid "Import Failure" -msgstr "" +msgstr "İçe Aktarma Hatası" #: ImportMerge/importmerge.py:407 msgid "Your Tree and import are the same." -msgstr "" +msgstr "Ağacınız ve içe aktardığınız dosyalar aynı." #: ImportMerge/importmerge.py:452 ImportMerge/importmerge.py:461 #: ImportMerge/importmerge.py:469 ImportMerge/importmerge.py:477 msgid "your tree " -msgstr "" +msgstr "ağacınız " #: ImportMerge/importmerge.py:459 ImportMerge/importmerge.py:467 #: ImportMerge/importmerge.py:475 msgid "imported " -msgstr "" +msgstr "içe aktarılmış " #: ImportMerge/importmerge.py:482 ImportMerge/importmerge.py:962 #: ImportMerge/importmerge.py:983 msgid "Original" -msgstr "" +msgstr "Orijinal" #: ImportMerge/importmerge.py:483 ImportMerge/importmerge.py:963 #: ImportMerge/importmerge.py:984 msgid "Imported" -msgstr "" +msgstr "İçe aktarılmış" #: ImportMerge/importmerge.py:485 ImportMerge/importmerge.py:965 #: ImportMerge/importmerge.py:986 msgid "Result " -msgstr "" +msgstr "Sonuç " #: ImportMerge/importmerge.py:711 msgid "Import and Merge" -msgstr "" +msgstr "İçe Aktar ve Birleştir" #: JSON/JSON.gpr.py:4 msgid "JSON Export" -msgstr "" +msgstr "JSON Dışa Aktarma" #: JSON/JSON.gpr.py:5 msgid "This is a JSON export" -msgstr "" +msgstr "Bu bir JSON dışa aktarımıdır" #: JSON/JSON.gpr.py:12 msgid "JSON options" -msgstr "" +msgstr "JSON seçenekleri" #: JSON/JSON.gpr.py:20 msgid "JSON Import" -msgstr "" +msgstr "JSON İçe Aktarma" #: JSON/JSON.gpr.py:21 msgid "This is a JSON import" -msgstr "" +msgstr "Bu bir JSON içe aktarmadır" #: JSON/JSONImport.py:69 msgid "JSON import" -msgstr "" +msgstr "JSON içe aktarma" #: LastChange/LastChange.gpr.py:26 msgid "List the last ten person records that have been changed" -msgstr "" +msgstr "Son on kişi kaydında yapılan değişiklikleri listeleyin" #: LastChange/LastChange.gpr.py:35 msgid "Latest Changes" -msgstr "" +msgstr "Son Değişiklikler" #: LastChange/LastChange.gpr.py:42 LastChange/LastChangeReport.py:76 #: LastChange/LastChangeReport.py:115 msgid "Last Change Report" -msgstr "" +msgstr "Son Değişiklik Raporu" #: LastChange/LastChange.gpr.py:43 msgid "Report of the last records that have been changed" -msgstr "" +msgstr "Değiştirilen son kayıtların raporu" #: LastChange/LastChangeGramplet.py:82 msgid "changed on" -msgstr "değişti" +msgstr "değiştirildi" #: LastChange/LastChangeReport.py:77 msgid "You must select at least one type of record." -msgstr "" +msgstr "En az bir kayıt türü seçmelisiniz." #: LastChange/LastChangeReport.py:181 msgid "People Changed" -msgstr "" +msgstr "İnsanlar Değiştirildi" #: LastChange/LastChangeReport.py:182 LastChange/LastChangeReport.py:202 #: LastChange/LastChangeReport.py:234 LastChange/LastChangeReport.py:258 #: LastChange/LastChangeReport.py:278 LastChange/LastChangeReport.py:299 #: LastChange/LastChangeReport.py:317 LastChange/LastChangeReport.py:337 msgid "Changed On" -msgstr "" +msgstr "Değiştirildi" #: LastChange/LastChangeReport.py:201 msgid "Families Changed" -msgstr "" +msgstr "Aileler Değiştirildi" #: LastChange/LastChangeReport.py:202 msgid "Family Surname" -msgstr "" +msgstr "Aile Soyadı" #: LastChange/LastChangeReport.py:219 #, python-format msgid "%s and %s" -msgstr "" +msgstr "%s ve %s" #: LastChange/LastChangeReport.py:233 msgid "Events Changed" -msgstr "" +msgstr "Etkinlikler Değiştirildi" #: LastChange/LastChangeReport.py:257 msgid "Places Changed" -msgstr "" +msgstr "Yerler Değiştirildi" #: LastChange/LastChangeReport.py:277 msgid "Media Changed" -msgstr "" +msgstr "Medya Değiştirildi" #: LastChange/LastChangeReport.py:298 msgid "Sources Changed" -msgstr "" +msgstr "Kaynaklar Değiştirildi" #: LastChange/LastChangeReport.py:316 msgid "Notes Changed" -msgstr "Notlar değişti" +msgstr "Notlar Değiştirildi" #: LastChange/LastChangeReport.py:336 msgid "Citations Changed" -msgstr "" +msgstr "Alıntılar Değiştirildi" #: LastChange/LastChangeReport.py:362 msgid "Select From" -msgstr "" +msgstr "Şuradan seçin" #: LastChange/LastChangeReport.py:422 msgid "The style used for normal text" -msgstr "" +msgstr "Normal metin için kullanılan stil" #: LifeLineChartView/_dummy_translation_string_po.py:10 msgid "Warp the chart shape" -msgstr "" +msgstr "Grafik şeklini bük" #: LifeLineChartView/_dummy_translation_string_po.py:11 msgid "The overall shape of the chart can be warped." -msgstr "" +msgstr "Grafiğin genel şekli bükülebilir." #: LifeLineChartView/_dummy_translation_string_po.py:12 msgid "Normal grid shape" -msgstr "" +msgstr "Normal ızgara şekli" #: LifeLineChartView/_dummy_translation_string_po.py:13 msgid "Sine shape" -msgstr "" +msgstr "Sinüs şekli" #: LifeLineChartView/_dummy_translation_string_po.py:14 msgid "Triangular shape" -msgstr "" +msgstr "Üçgen şekil" #: LifeLineChartView/_dummy_translation_string_po.py:15 msgid "Show photos" -msgstr "" +msgstr "Fotoğrafları göster" #: LifeLineChartView/_dummy_translation_string_po.py:16 msgid "Photos of the individual are shown." -msgstr "" +msgstr "Bireyin fotoğrafları gösterilmektedir." #: LifeLineChartView/_dummy_translation_string_po.py:17 msgid "Photo size" -msgstr "" +msgstr "Fotoğraf boyutu" #: LifeLineChartView/_dummy_translation_string_po.py:18 msgid "" "Photos which are shown are fitted into a square of this extent. The size is " "given relative to the line thickness." msgstr "" +"Gösterilen fotoğraflar bu boyuttaki bir kareye sığdırılmıştır. Boyut, çizgi " +"kalınlığına göre verilmiştir." #: LifeLineChartView/_dummy_translation_string_po.py:19 msgid "Total height" -msgstr "" +msgstr "Toplam yükseklik" #: LifeLineChartView/_dummy_translation_string_po.py:20 msgid "Total height of the whole chart." -msgstr "" +msgstr "Tüm grafiğin toplam yüksekliği." #: LifeLineChartView/_dummy_translation_string_po.py:21 msgid "Relative line thickness" -msgstr "" +msgstr "Göreceli çizgi kalınlığı" #: LifeLineChartView/_dummy_translation_string_po.py:22 msgid "" "The line thickness of an individual is given relatively to the horizontal " "step size." -msgstr "" +msgstr "Bireysel çizgi kalınlığı, yatay adım boyutuna göre verilmiştir." #: LifeLineChartView/_dummy_translation_string_po.py:23 msgid "Horizontal step size" -msgstr "" +msgstr "Yatay adım boyutu" #: LifeLineChartView/_dummy_translation_string_po.py:24 msgid "" "This is the distance from one line to another. This value is also used for " "scaling of other items." msgstr "" +"Bu, bir çizgiden diğerine olan mesafedir. Bu değer, diğer öğelerin " +"ölçeklendirilmesi için de kullanılır." #: LifeLineChartView/_dummy_translation_string_po.py:25 msgid "Show birth label" -msgstr "" +msgstr "Doğum etiketini göster" #: LifeLineChartView/_dummy_translation_string_po.py:26 msgid "Activate the birth label." -msgstr "" +msgstr "Doğum etiketini etkinleştirin." #: LifeLineChartView/_dummy_translation_string_po.py:27 msgid "Birth label along path" -msgstr "" +msgstr "Yol boyunca doğum etiketi" #: LifeLineChartView/_dummy_translation_string_po.py:28 msgid "The birth label is aligned to the individual line." -msgstr "" +msgstr "Doğum etiketi, birey çizgisine hizalanır." #: LifeLineChartView/_dummy_translation_string_po.py:29 #: LifeLineChartView/_dummy_translation_string_po.py:32 @@ -17568,232 +17720,244 @@ msgstr "" #: LifeLineChartView/_dummy_translation_string_po.py:77 #: LifeLineChartView/lifelinechartview.py:884 msgid "Label Configuration" -msgstr "" +msgstr "Etiket Yapılandırması" #: LifeLineChartView/_dummy_translation_string_po.py:30 msgid "Birth label rotation" -msgstr "" +msgstr "Doğum etiketi döndürme" #: LifeLineChartView/_dummy_translation_string_po.py:31 msgid "The birth label is written in a text frame rotated by this value." msgstr "" +"Doğum etiketi, bu değer kadar döndürülmüş bir metin çerçevesine yazılır." #: LifeLineChartView/_dummy_translation_string_po.py:33 msgid "Horizontal offset of birth label" -msgstr "" +msgstr "Doğum etiketinin yatay kaydırması" #: LifeLineChartView/_dummy_translation_string_po.py:34 msgid "" "The birth label is moved horizontally by this value, which is given " "relatively to the font size." msgstr "" +"Doğum etiketi, yazı tipi boyutuna göre verilen bu değer kadar yatay olarak " +"hareket ettirilir." #: LifeLineChartView/_dummy_translation_string_po.py:36 msgid "Vertical offset of birth label" -msgstr "" +msgstr "Doğum etiketinin dikey kaydırması" #: LifeLineChartView/_dummy_translation_string_po.py:37 msgid "" "The birth label is moved vertically by this value, which is given relatively " "to the font size." msgstr "" +"Doğum etiketi, yazı tipi boyutuna göre verilen bu değer kadar dikey olarak " +"hareket ettirilir." #: LifeLineChartView/_dummy_translation_string_po.py:39 msgid "Wrap words in birth label" -msgstr "" +msgstr "Doğum etiketindeki kelimeleri sar" #: LifeLineChartView/_dummy_translation_string_po.py:40 msgid "The birth label content is wrapped where possible." -msgstr "" +msgstr "Doğum etiketi içeriği, mümkün olan yerlerde sarılır." #: LifeLineChartView/_dummy_translation_string_po.py:42 msgid "Birth label anchor" -msgstr "" +msgstr "Doğum etiketi bağlantısı" #: LifeLineChartView/_dummy_translation_string_po.py:43 msgid "Text alignment of the birth label." -msgstr "" +msgstr "Doğum etiketinin metin hizalaması." #: LifeLineChartView/_dummy_translation_string_po.py:45 #: LifeLineChartView/_dummy_translation_string_po.py:65 msgid "Center" -msgstr "" +msgstr "Ortala" #: LifeLineChartView/_dummy_translation_string_po.py:48 msgid "Show death label" -msgstr "" +msgstr "Ölüm etiketini göster" #: LifeLineChartView/_dummy_translation_string_po.py:49 msgid "Activate the death label." -msgstr "" +msgstr "Ölüm etiketini etkinleştir." #: LifeLineChartView/_dummy_translation_string_po.py:50 msgid "Death label rotation" -msgstr "" +msgstr "Ölüm etiketi döndürme" #: LifeLineChartView/_dummy_translation_string_po.py:51 msgid "The death label is written in a text frame rotated by this value." -msgstr "" +msgstr "Ölüm etiketi, bu değer kadar döndürülmüş bir metin çerçevesine yazılır." #: LifeLineChartView/_dummy_translation_string_po.py:53 msgid "Horizontal offset of death label" -msgstr "" +msgstr "Ölüm etiketinin yatay kaydırma mesafesi" #: LifeLineChartView/_dummy_translation_string_po.py:54 msgid "" "The death label is moved horizontally by this value, which is given " "relatively to the font size." msgstr "" +"Ölüm etiketi, yazı tipi boyutuna göre verilen bu değer kadar yatay olarak " +"hareket ettirilir." #: LifeLineChartView/_dummy_translation_string_po.py:56 msgid "Vertical offset of death label" -msgstr "" +msgstr "Ölüm etiketinin dikey kaydırma mesafesi" #: LifeLineChartView/_dummy_translation_string_po.py:57 msgid "" "The death label is moved vertically by this value, which is given relatively " "to the font size." msgstr "" +"Ölüm etiketi, yazı tipi boyutuna göre verilen bu değer kadar dikey olarak " +"hareket ettirilir." #: LifeLineChartView/_dummy_translation_string_po.py:59 msgid "Wrap words in death label" -msgstr "" +msgstr "Ölüm etiketindeki kelimeleri sar" #: LifeLineChartView/_dummy_translation_string_po.py:60 msgid "The death label content is wrapped where possible." -msgstr "" +msgstr "Ölüm etiketi içeriği, mümkün olan yerlerde sarılır." #: LifeLineChartView/_dummy_translation_string_po.py:62 msgid "Death label anchor" -msgstr "" +msgstr "Ölüm etiketi bağlantısı" #: LifeLineChartView/_dummy_translation_string_po.py:63 msgid "Text alignment of the death label." -msgstr "" +msgstr "Ölüm etiketinin metin hizalaması." #: LifeLineChartView/_dummy_translation_string_po.py:68 msgid "Show marriage label" -msgstr "" +msgstr "Evlilik etiketini göster" #: LifeLineChartView/_dummy_translation_string_po.py:69 msgid "Activate the marriage label." -msgstr "" +msgstr "Evlilik etiketini etkinleştir." #: LifeLineChartView/_dummy_translation_string_po.py:70 msgid "Fade individual color" -msgstr "" +msgstr "Birey rengini soldur" #: LifeLineChartView/_dummy_translation_string_po.py:71 msgid "The color of the individuals is faded to black with increasing age." -msgstr "" +msgstr "Bireylerin rengi, yaş arttıkça siyaha doğru soldurulur." #: LifeLineChartView/_dummy_translation_string_po.py:72 msgid "Font name" -msgstr "" +msgstr "Yazı tipi adı" #: LifeLineChartView/_dummy_translation_string_po.py:73 msgid "Name of the font family used for labels." -msgstr "" +msgstr "Etiketler için kullanılan yazı tipi ailesinin adı." #: LifeLineChartView/_dummy_translation_string_po.py:75 msgid "Relative font size" -msgstr "" +msgstr "Göreceli yazı tipi boyutu" #: LifeLineChartView/_dummy_translation_string_po.py:76 msgid "The font size is given relatively to the line thickness." -msgstr "" +msgstr "Yazı tipi boyutu, çizgi kalınlığına göre verilir." #: LifeLineChartView/_dummy_translation_string_po.py:78 msgid "Family shape" -msgstr "" +msgstr "Aile şekli" #: LifeLineChartView/_dummy_translation_string_po.py:79 msgid "The shape of the families can be varied." -msgstr "" +msgstr "Ailelerin şekli değiştirilebilir." #: LifeLineChartView/_dummy_translation_string_po.py:80 msgid "Rectangular" -msgstr "" +msgstr "Dikdörtgen" #: LifeLineChartView/_dummy_translation_string_po.py:81 msgid "Softer" -msgstr "" +msgstr "Daha yumuşak" #: LifeLineChartView/_dummy_translation_string_po.py:82 msgid "Upper year minimum margin" -msgstr "" +msgstr "Üst yıl minimum marjı" #: LifeLineChartView/_dummy_translation_string_po.py:83 msgid "" "The upper bound of the chart is extended by at least this number of years." -msgstr "" +msgstr "Grafiğin üst sınırı en az bu yıl sayısı kadar genişletilir." #: LifeLineChartView/_dummy_translation_string_po.py:84 msgid "Lower year minimum margin" -msgstr "" +msgstr "Alt yıl minimum marjı" #: LifeLineChartView/_dummy_translation_string_po.py:85 msgid "" "The lower bound of the chart is extended by at least this number of years." -msgstr "" +msgstr "Grafiğin alt sınırı en az bu kadar yıl genişletilir." #: LifeLineChartView/_dummy_translation_string_po.py:86 msgid "Visualize connections" -msgstr "" +msgstr "Bağlantıları görselleştirin" #: LifeLineChartView/_dummy_translation_string_po.py:87 msgid "" "Visualize the connections between families and individuals. This is for " "debugging purposes." msgstr "" +"Aileler ve bireyler arasındaki bağlantıları görselleştirin. Bu, hata " +"ayıklama amaçlıdır." #: LifeLineChartView/_dummy_translation_string_po.py:88 msgid "Visualize ambiguous placement" -msgstr "" +msgstr "Belirsiz yerleşimi görselleştirin" #: LifeLineChartView/_dummy_translation_string_po.py:89 msgid "Visualize automatically detected errors in the placement algorithm." msgstr "" +"Yerleştirme algoritmasında otomatik olarak algılanan hataları görselleştirin." #: LifeLineChartView/_dummy_translation_string_po.py:90 msgid "Coloring of individuals" -msgstr "" +msgstr "Bireylerin renklendirilmesi" #: LifeLineChartView/_dummy_translation_string_po.py:91 msgid "There are different ways to determine the color of an individual." -msgstr "" +msgstr "Bir bireyin rengini belirlemenin farklı yolları vardır." #: LifeLineChartView/_dummy_translation_string_po.py:92 msgid "Unique" -msgstr "" +msgstr "Benzersiz" #: LifeLineChartView/_dummy_translation_string_po.py:93 msgid "Based on surname" -msgstr "" +msgstr "Soyadına göre" #: LifeLineChartView/_dummy_translation_string_po.py:94 msgid "Flip vertically" -msgstr "" +msgstr "Dikey olarak çevir" #: LifeLineChartView/_dummy_translation_string_po.py:95 msgid "If active, the chart is flipped vertically." -msgstr "" +msgstr "Etkinse, grafik dikey olarak çevrilir." #: LifeLineChartView/_dummy_translation_string_po.py:96 msgid "Line weighting" -msgstr "" +msgstr "Çizgi kalınlığı" #: LifeLineChartView/_dummy_translation_string_po.py:97 msgid "The line width can be configured." -msgstr "" +msgstr "Çizgi kalınlığı yapılandırılabilir." #: LifeLineChartView/_dummy_translation_string_po.py:98 msgid "none" -msgstr "" +msgstr "yok" #: LifeLineChartView/_dummy_translation_string_po.py:99 msgid "Show pedigree collapse" -msgstr "" +msgstr "Soy ağacı daralmasını göster" #: LifeLineChartView/_dummy_translation_string_po.py:100 msgid "" @@ -17801,42 +17965,46 @@ msgid "" "simple ancestor chart. If this is active, then every individual only appears " "once." msgstr "" +"Soy ağacı daralması nedeniyle, bir birey basit bir soy ağacında birkaç kez " +"görünebilir. Bu etkinse, her birey yalnızca bir kez görünür." #: LifeLineChartView/_dummy_translation_string_po.py:102 msgid "" "if this number of generations has been reached, the algorithm doesn't go any " "deeper" -msgstr "" +msgstr "Bu nesil sayısına ulaşıldığında, algoritma daha derine inmez" #: LifeLineChartView/_dummy_translation_string_po.py:103 msgid "Fathers have the same color" -msgstr "" +msgstr "Babalar aynı renkte" #: LifeLineChartView/_dummy_translation_string_po.py:104 msgid "" "Starting from the root person, each father of an added individual has the " "same color as that individual." msgstr "" +"Kök kişiden başlayarak, eklenen her bireyin babası, o bireyle aynı renktedir." #: LifeLineChartView/_dummy_translation_string_po.py:105 msgid "Maximum number of compression steps" -msgstr "" +msgstr "Maksimum sıkıştırma adımı sayısı" #: LifeLineChartView/_dummy_translation_string_po.py:106 msgid "Debugging of the compression algorithm. Abort after x steps." -msgstr "" +msgstr "Sıkıştırma algoritmasının hata ayıklaması. x adımdan sonra iptal et." #: LifeLineChartView/_dummy_translation_string_po.py:107 msgid "Maximum number of flipping steps" -msgstr "" +msgstr "Maksimum çevirme adımı sayısı" #: LifeLineChartView/_dummy_translation_string_po.py:108 msgid "Debugging of the flipping algorithm. Abort after x steps." msgstr "" +"Çevirme algoritmasının hata ayıklaması. x adımdan sonra işlemi iptal et." #: LifeLineChartView/_dummy_translation_string_po.py:109 msgid "Compress the chart horizontally" -msgstr "" +msgstr "Grafiği yatay olarak sıkıştır" #: LifeLineChartView/_dummy_translation_string_po.py:110 msgid "" @@ -17844,10 +18012,13 @@ msgid "" "inefficient with many generations. This algorithm lets several people share " "a horizontal slot, if they do not overlap." msgstr "" +"Varsayılan olarak her bireyin benzersiz bir yatay yuvası vardır. Bu, birçok " +"nesilde verimsiz olabilir. Bu algoritma, çakışmadıkları takdirde birkaç " +"kişinin yatay bir yuvayı paylaşmasına olanak tanır." #: LifeLineChartView/_dummy_translation_string_po.py:111 msgid "Flip families to reduce horizontal connections" -msgstr "" +msgstr "Yatay bağlantıları azaltmak için aileleri çevirin" #: LifeLineChartView/_dummy_translation_string_po.py:112 msgid "" @@ -17855,207 +18026,214 @@ msgid "" "horizontal cross connections in larger graphs. This is happens if one person " "is shown in more than one family or it is caused by pedigree collapse." msgstr "" +"Daha büyük grafiklerde genel yatay çapraz bağlantıları azaltmak için bir " +"ailedeki anne ve babanın konumunu değiştirin. Bu, bir kişi birden fazla " +"ailede gösteriliyorsa veya soy ağacı çökmesinden kaynaklanıyorsa olur." #: LifeLineChartView/_dummy_translation_string_po.py:113 msgid "Highlight descandants" -msgstr "" +msgstr "Soydan gelenleri vurgula" #: LifeLineChartView/_dummy_translation_string_po.py:114 msgid "Highlight the persons which are descendants." -msgstr "" +msgstr "Soydan gelen kişileri vurgulayın." #: LifeLineChartView/_dummy_translation_string_po.py:115 #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.gpr.py:4 msgid "Number of descendants" -msgstr "" +msgstr "Soydan gelen sayısı" #: LifeLineChartView/_dummy_translation_string_po.py:116 msgid "Very soft" -msgstr "" +msgstr "Çok yumuşak" #: LifeLineChartView/_dummy_translation_string_po.py:117 msgid "General chart layout" -msgstr "" +msgstr "Genel grafik düzeni" #: LifeLineChartView/_dummy_translation_string_po.py:118 msgid "There are different layouts for descendant charts." -msgstr "" +msgstr "Soydan gelen grafikler için farklı düzenler vardır." #: LifeLineChartView/_dummy_translation_string_po.py:119 msgid "Parents enclose children" -msgstr "" +msgstr "Ebeveynler çocukları çevreler" #: LifeLineChartView/_dummy_translation_string_po.py:120 msgid "Cactus" -msgstr "" +msgstr "Kaktüs" #: LifeLineChartView/lifelinechart.py:75 msgid "" "LifeLineChartView dependencies are missing. It is not possible to use this " "plugin without them." msgstr "" +"LifeLineChartView bağımlılıkları eksik. Bunlar olmadan bu eklentiyi " +"kullanmak mümkün değil." #: LifeLineChartView/lifelinechart.py:1183 #, python-brace-format msgid "cursor position at {date}" -msgstr "" +msgstr "İmleç konumu {date}" #: LifeLineChartView/lifelinechart.py:2591 msgid "Hide person" -msgstr "" +msgstr "Kişiyi gizle" #: LifeLineChartView/lifelinechart.py:2597 msgid "Show person" -msgstr "" +msgstr "Kişiyi göster" #: LifeLineChartView/lifelinechart.py:2622 msgid "Show siblings" -msgstr "" +msgstr "Kardeşleri göster" #: LifeLineChartView/lifelinechart.py:2627 msgid "Hide siblings" -msgstr "" +msgstr "Kardeşleri gizle" #: LifeLineChartView/lifelinechart.py:2652 msgid "Show ancestors above " -msgstr "" +msgstr "Üstteki ataları göster " #: LifeLineChartView/lifelinechartview.gpr.py:24 msgid "Life Line Ancestor Chart" -msgstr "" +msgstr "Yaşam Çizgisi Atalar Grafiği" #: LifeLineChartView/lifelinechartview.gpr.py:26 #: LifeLineChartView/lifelinechartview.gpr.py:44 msgid "Persons and their relation in a time based chart" -msgstr "" +msgstr "Zaman bazlı grafikteki kişiler ve ilişkileri" #: LifeLineChartView/lifelinechartview.gpr.py:34 #: LifeLineChartView/lifelinechartview.gpr.py:52 #: LifeLineChartView/lifelinechartview.py:399 msgid "Life Line Chart" -msgstr "" +msgstr "Yaşam Çizgisi Grafiği" #: LifeLineChartView/lifelinechartview.gpr.py:42 msgid "Life Line Descendant Chart" -msgstr "" +msgstr "Yaşam Çizgisi Soyundan Gelen Grafiği" #: LifeLineChartView/lifelinechartview.py:386 #: LifeLineChartView/lifelinechartview.py:883 msgid "General Layout" -msgstr "" +msgstr "Genel Düzen" #: LifeLineChartView/lifelinechartview.py:509 #: LifeLineChartView/lifelinechartview.py:510 #: LifeLineChartView/lifelinechartview.py:519 #: LifeLineChartView/lifelinechartview.py:520 msgid "Ctrl" -msgstr "" +msgstr "Ctrl" #: LifeLineChartView/lifelinechartview.py:510 #: LifeLineChartView/lifelinechartview.py:520 msgid "Mouse wheel" -msgstr "" +msgstr "Fare tekerleği" #: LifeLineChartView/lifelinechartview.py:541 msgid "Rebuild data cache" -msgstr "" +msgstr "Veri önbelleğini yeniden oluştur" #: LifeLineChartView/lifelinechartview.py:548 msgid "Revert the placement and person selection" -msgstr "" +msgstr "Yerleşimi ve kişi seçimini geri al" #: LifeLineChartView/lifelinechartview.py:564 msgid "Open Life Line Chart help" -msgstr "" +msgstr "Yaşam Çizgisi Grafiği yardımını aç" #: LifeLineChartView/lifelinechartview.py:831 msgid "Export View as SVG" -msgstr "" +msgstr "Görünümü SVG olarak dışa aktar" #: LifeLineChartView/lifelinechartview.py:946 msgid "Reset all settings" -msgstr "" +msgstr "Tüm ayarları sıfırla" #: LinesOfDescendency/lines-of-descendency.gpr.py:31 msgid "Lines of Descendency Report" -msgstr "" +msgstr "Soy Kütüğü Raporu" #: LinesOfDescendency/lines-of-descendency.gpr.py:33 msgid "" "Prints out all descendency lines from a given ancestor to a given descendent " "in text." msgstr "" +"Belirtilen bir atadan belirtilen bir toruna kadar uzanan tüm soy hatlarını " +"metin olarak yazdırır." #: LinesOfDescendency/lines-of-descendency.py:57 msgid "The ancestor from which to start the line" -msgstr "" +msgstr "Soyun başlangıç noktası olan atası" #: LinesOfDescendency/lines-of-descendency.py:60 msgid "Descendent" -msgstr "" +msgstr "Soyundan gelen" #: LinesOfDescendency/lines-of-descendency.py:61 msgid "The descendent to which to build the line" -msgstr "" +msgstr "Soyundan gelen ağacını oluşturmak için kullanılacak torun" #: LinesOfDescendency/lines-of-descendency.py:83 msgid "The style used for the title of a line." -msgstr "" +msgstr "Bir satırın başlığı için kullanılan stil." #: LinesOfDescendency/lines-of-descendency.py:114 #, python-format msgid "%(line)s. line:" -msgstr "" +msgstr "%(line)s. satır:" #: LinesOfDescendency/lines-of-descendency.py:186 #, python-format msgid "Lines of Descendency from %(ancestor)s to %(descendent)s" -msgstr "" +msgstr "%(ancestor)s > %(descendent)s uzanan soy hatları" #: ListeEclair/ListeEclair.gpr.py:4 ListeEclair/ListeEclair.py:97 #: ListeEclair/ListeEclair.py:104 msgid "Liste Eclair" -msgstr "" +msgstr "Hızlı Liste" #: ListeEclair/ListeEclair.gpr.py:5 msgid "Produit une liste eclair" -msgstr "" +msgstr "Hızlı bir liste oluşturur" #: ListeEclair/ListeEclair.py:277 msgid "Type de Liste" -msgstr "" +msgstr "Liste Türü" #: ListeEclair/ListeEclair.py:279 msgid "Tiny Tafel" -msgstr "" +msgstr "Minik Levha" #: ListeEclair/ListeEclair.py:280 msgid "cousingenweb" -msgstr "" +msgstr "cousingenweb" #: ListeEclair/ListeEclair.py:281 msgid "Type de liste" -msgstr "" +msgstr "Liste türü" #: ListeEclair/ListeEclair.py:284 msgid "Include private data" -msgstr "" +msgstr "Özel verileri dahil et" #: ListeEclair/ListeEclair.py:301 msgid "The style used for the liste eclair." -msgstr "" +msgstr "Hızlı liste için kullanılan stil." #: ListeEclair/ListeEclair.py:314 msgid "The style used for place title." -msgstr "" +msgstr "Yer adları için kullanılan stil." #: MediaBrowser/MediaBrowser.gpr.py:31 msgid "Media Browser" -msgstr "" +msgstr "Medya Tarayıcı" #: MediaBrowser/MediaBrowser.gpr.py:39 msgid "Browser" -msgstr "" +msgstr "Tarayıcı" #: MediaMerge/mediamerge.gpr.py:31 msgid "Merge Media" @@ -18066,6 +18244,7 @@ msgid "" "Searches the entire database, looking for media that have the same path and " "merges them." msgstr "" +"Veritabanının tamamını tarar, aynı yola sahip medyaları bulur ve birleştirir." #: MediaMerge/mediamerge.py:104 msgid "Media Merge" @@ -18083,194 +18262,200 @@ msgstr "Hiçbir ortam ögesi birleştirilmedi." #: MediaReport/media_report.gpr.py:24 msgid "Media Report" -msgstr "" +msgstr "Medya Raporu" #: MediaReport/media_report.gpr.py:25 msgid "Generates report including images, image data and notes." -msgstr "" +msgstr "Resimleri, resim verilerini ve notları içeren bir rapor oluşturur." #: MediaReport/media_report.py:106 MediaReport/media_report.py:119 msgid "You have to select an image to generate this report." -msgstr "" +msgstr "Bu raporu oluşturmak için bir resim seçmeniz gerekiyor." #: MediaReport/media_report.py:112 msgid "" "You have to select a custom note or uncheck the option 'include custom note' " "to generate this report." msgstr "" +"Bu raporu oluşturmak için özel bir not seçmeniz veya 'özel not ekle' " +"seçeneğinin işaretini kaldırmanız gerekmektedir." #: MediaReport/media_report.py:125 msgid "This report only supports PDF as output file format." -msgstr "" +msgstr "Bu rapor yalnızca PDF dosya biçimini desteklemektedir." #: MediaReport/media_report.py:229 msgid "General:" -msgstr "" +msgstr "Genel:" #: MediaReport/media_report.py:250 msgid "Image type:" -msgstr "" +msgstr "Resim türü:" #: MediaReport/media_report.py:426 msgid "Heading" -msgstr "" +msgstr "Başlık" #: MediaReport/media_report.py:430 msgid "Select a media file for this report" -msgstr "" +msgstr "Bu rapor için bir medya dosyası seçin" #: MediaReport/media_report.py:433 msgid "Custom note" -msgstr "" +msgstr "Özel not" #: MediaReport/media_report.py:434 msgid "Select a note for this report" -msgstr "" +msgstr "Bu rapor için bir not seçin" #: MediaReport/media_report.py:437 msgid "Include custom note" -msgstr "" +msgstr "Özel notu dahil et" #: MediaReport/media_report.py:438 msgid "The custom note will be included" -msgstr "" +msgstr "Özel not dahil edilecektir" #: MediaReport/media_report.py:442 msgid "Include referenced people" -msgstr "" +msgstr "Referans verilen kişileri dahil et" #: MediaReport/media_report.py:443 msgid "Referenced people will be included" -msgstr "" +msgstr "Referans verilen kişiler dahil edilecektir" #: MediaReport/media_report.py:446 msgid "Include media data" -msgstr "" +msgstr "Medya verilerini dahil et" #: MediaReport/media_report.py:447 msgid "Tags, notes and attributes will be included" -msgstr "" +msgstr "Etiketler, notlar ve öznitelikler dahil edilecektir" #: MediaReport/media_report.py:450 msgid "Media width" -msgstr "" +msgstr "Medya genişliği" #: MediaReport/media_report.py:452 #, no-python-format msgid "Maximum media width in % of available page width." -msgstr "" +msgstr "Mevcut sayfa genişliğinin %'si olarak maksimum medya genişliği." #: MediaReport/media_report.py:456 msgid "Media height" -msgstr "" +msgstr "Medya yüksekliği" #: MediaReport/media_report.py:458 #, no-python-format msgid "Maximum media height in % of available page height." -msgstr "" +msgstr "Mevcut sayfa yüksekliğinin %'si olarak maksimum medya yüksekliği." #: MediaVerify/MediaVerify.gpr.py:31 msgid "Media Verify" -msgstr "" +msgstr "Medya Doğrulama" #: MediaVerify/MediaVerify.gpr.py:32 msgid "Verify that media is present in the correct path" -msgstr "" +msgstr "Medyanın doğru yolda bulunduğunu doğrulayın" #: MediaVerify/MediaVerify.py:77 msgid "Media Verify Tool" -msgstr "" +msgstr "Medya Doğrulama Aracı" #: MediaVerify/MediaVerify.py:82 msgid "Missing Files" -msgstr "" +msgstr "Eksik Dosyalar" #: MediaVerify/MediaVerify.py:82 msgid "Moved/Renamed Files" -msgstr "" +msgstr "Taşınan/Yeniden Adlandırılan Dosyalar" #: MediaVerify/MediaVerify.py:83 msgid "Duplicate Files" -msgstr "" +msgstr "Yinelenen Dosyalar" #: MediaVerify/MediaVerify.py:83 msgid "Extra Files" -msgstr "" +msgstr "Fazladan Dosyalar" #: MediaVerify/MediaVerify.py:84 msgid "Errors" -msgstr "" +msgstr "Hatalar" #: MediaVerify/MediaVerify.py:84 msgid "No md5 Generated" -msgstr "" +msgstr "md5 Oluşturulmadı" #: MediaVerify/MediaVerify.py:100 msgid "Close the Media Verify Tool" -msgstr "" +msgstr "Medya Doğrulama Aracını Kapat" #: MediaVerify/MediaVerify.py:102 PDFForms/generatepdfform.py:129 msgid "Generate" -msgstr "" +msgstr "Oluştur" #: MediaVerify/MediaVerify.py:103 msgid "Generate md5 hashes for media objects" -msgstr "" +msgstr "Medya nesneleri için md5 özetleri oluşturun" #: MediaVerify/MediaVerify.py:105 msgid "Verify" -msgstr "" +msgstr "Doğrula" #: MediaVerify/MediaVerify.py:106 msgid "Check media paths and report missing, duplicate and extra files" msgstr "" +"Medya yollarını kontrol edin ve eksik, yinelenen ve fazladan dosyaları " +"raporlayın" #: MediaVerify/MediaVerify.py:110 NoteCleanup/NoteCleanup.py:136 msgid "Export the results to a text file" -msgstr "" +msgstr "Sonuçları bir metin dosyasına aktarın" #: MediaVerify/MediaVerify.py:112 msgid "Fix" -msgstr "" +msgstr "Düzelt" #: MediaVerify/MediaVerify.py:113 msgid "Fix media paths of moved and renamed files" -msgstr "" +msgstr "Taşınan ve yeniden adlandırılan dosyaların medya yollarını düzeltin" #: MediaVerify/MediaVerify.py:131 msgid "Verify Gramps media using md5 hashes" -msgstr "" +msgstr "md5 özetlerini kullanarak Gramps medyasını doğrulayın" #: MediaVerify/MediaVerify.py:142 msgid "Files" -msgstr "" +msgstr "Dosyalar" #: MediaVerify/MediaVerify.py:194 NoteCleanup/NoteCleanup.py:323 msgid "Export results to a text file" -msgstr "" +msgstr "Sonuçları bir metin dosyasına aktarın" #: MediaVerify/MediaVerify.py:217 NoteCleanup/NoteCleanup.py:346 #, python-format msgid "Error when writing the report: %s" -msgstr "" +msgstr "Rapor yazılırken hata: %s" #: MediaVerify/MediaVerify.py:258 msgid "Generating media hashes" -msgstr "" +msgstr "Medya özetleri oluşturuluyor" #: MediaVerify/MediaVerify.py:260 msgid "Set media hashes" -msgstr "" +msgstr "Medya özetlerini ayarlayın" #: MediaVerify/MediaVerify.py:295 msgid "" "Media path not set. You must set the \"Base path for relative media paths\" " "in the Preferences." msgstr "" +"Medya yolu ayarlanmadı. Tercihlerde \"Göreli medya yolları için temel " +"yol\"u ayarlamanız gerekir." #: MediaVerify/MediaVerify.py:306 msgid "Finding files" -msgstr "" +msgstr "Dosyaları bulma" #: MediaVerify/MediaVerify.py:334 msgid "Checking paths" @@ -18278,73 +18463,71 @@ msgstr "Yolları kontrol etme" #: MediaVerify/MediaVerify.py:387 msgid "Fixing file paths" -msgstr "" +msgstr "Yollar kontrol ediliyor" #: MediaVerify/MediaVerify.py:389 msgid "Fix media paths" -msgstr "" +msgstr "Medya yollarını düzeltin" #: MongoDB/mongodb.gpr.py:23 msgid "MongoDB" -msgstr "" +msgstr "MongoDB" #: MongoDB/mongodb.gpr.py:24 msgid "_MongoDB Database" -msgstr "" +msgstr "_MongoDB Veritabanı" #: MongoDB/mongodb.gpr.py:25 msgid "MongoDB Database" -msgstr "" +msgstr "MongoDB Veritabanı" #: NLWebConnectPack/NLWebPack.gpr.py:13 msgid "Collection of Web sites for the Netherlands (requires libwebconnect)" -msgstr "" +msgstr "Hollanda için Web siteleri koleksiyonu (libwebconnect gerektirir)" #: NLWebConnectPack/NLWebPack.py:34 msgid "Open Archives" -msgstr "" +msgstr "Arşivleri Aç" #: NLWebConnectPack/NLWebPack.py:35 msgid "Genealogy Online" -msgstr "" +msgstr "Çevrimiçi Şecere" #: NLWebConnectPack/NLWebPack.py:36 msgid "National Archive" -msgstr "" +msgstr "Ulusal Arşiv" #: NLWebConnectPack/NLWebPack.py:38 msgid "Who (re)searches who?" -msgstr "" +msgstr "Kim kimi (yeniden) arar?" #: NameSuite/.venv/lib/python3.12/site-packages/mypy/main.py:450 #, python-format msgid "%(prog)s: error: %(message)s\n" -msgstr "" +msgstr "%(prog)s: hata: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Given and Patronymic Names" -msgstr "Patronimik adlar:" +msgstr "Denetimde Verilen ve Patronimik Adlar" #: NameSuite/name_processor.gpr.py:9 msgid "" "Tools to rename given name, audit and infer patronymic (East Slavic) names." msgstr "" +"Verilen adları yeniden adlandırmak, denetlemek ve baba adlarını (Doğu Slav " +"adları) tahmin etmek için kullanılan araçlar." #: NameSuite/name_processor.gpr.py:25 NameSuite/name_processor.gpr.py:37 -#, fuzzy -#| msgid "Patronymic names:" msgid "Patronymic Suggestion" -msgstr "Patronimik adlar:" +msgstr "Baba Adı Önerisi" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." -msgstr "" +msgstr "Gezinirken gerçek zamanlı olarak (Doğu Slav) baba adlarını önerir." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" -msgstr "" +msgstr "Kullan" #: NameSuite/name_processor/views/gramplet.py:32 #, python-brace-format @@ -18353,320 +18536,288 @@ msgid "" "Suggested: {0}\n" "Based on father: {1}" msgstr "" +"Eksik patronimik tespit edildi.\n" +"Önerilen: {0}\n" +"Babaya göre: {1}" #: NameSuite/name_processor/views/gramplet.py:34 msgid "Navigate to an individual to check patronymic status." -msgstr "" +msgstr "Bir kişinin patronimik durumunu kontrol etmek için ona gidin." #: NameSuite/name_processor/views/gramplet.py:35 -#, fuzzy -#| msgid "No Active Person set." msgid "No active person selected." -msgstr "Aktif Kişi ayarlanmadı." +msgstr "Etkin kişi ayarlanmadı." #: NameSuite/name_processor/views/gramplet.py:37 msgid "" "Patronymic inference can't be inferred for non-binary or unknown genders." msgstr "" +"İkili cinsiyet sistemine uymayan veya cinsiyeti bilinmeyen kişiler için " +"patronimik çıkarımı yapılamaz." #: NameSuite/name_processor/views/gramplet.py:40 msgid "Individual already has a recorded patronymic." -msgstr "" +msgstr "Kişinin kayıtlı bir patronimik adı zaten mevcut." #: NameSuite/name_processor/views/gramplet.py:43 msgid "No attached father found in database family records." msgstr "" +"Veritabanındaki aile kayıtlarında babaya dair herhangi bir bilgi bulunamadı." #: NameSuite/name_processor/views/gramplet.py:46 msgid "Father lacks a recorded first name." -msgstr "" +msgstr "Babanın kayıtlı bir adı bulunmamaktadır." #: NameSuite/name_processor/views/gramplet.py:49 msgid "Could not generate valid morphology patterns." -msgstr "" +msgstr "Geçerli morfoloji kalıpları oluşturulamadı." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "" +msgstr "Patronimik adı başarıyla uygulandı!" #: NameSuite/name_processor/views/gramplet.py:77 msgid "Apply Suggestion" -msgstr "" +msgstr "Öneriyi Uygula" #: NameSuite/name_processor/views/tool.py:92 msgid "Infer East Slavic Patronymics" -msgstr "" +msgstr "Doğu Slav Patronimiklerini Çıkarımla" #: NameSuite/name_processor/views/tool.py:112 -#, fuzzy -#| msgid "Checking Given Names" msgid "Rename Given Names" -msgstr "Adlar kontrol ediliyor" +msgstr "Verilen Adları Yeniden Adlandır" #: NameSuite/name_processor/views/tool.py:115 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Patronymics" -msgstr "Patronimik adlar:" +msgstr "Patronimiklerin Denetimi" #: NameSuite/name_processor/views/tool_audit_tab.py:68 msgid "Auditing Settings" -msgstr "" +msgstr "Denetim Ayarları" #: NameSuite/name_processor/views/tool_audit_tab.py:76 msgid "All Records" -msgstr "" +msgstr "Tüm Kayıtlar" #: NameSuite/name_processor/views/tool_audit_tab.py:77 -#, fuzzy -#| msgid "Male line" msgid "Males Only" -msgstr "Erkek hattı" +msgstr "Sadece Erkekler" #: NameSuite/name_processor/views/tool_audit_tab.py:78 -#, fuzzy -#| msgid "Female line" msgid "Females Only" -msgstr "Kadın hattı" +msgstr "Sadece Kadınlar" #: NameSuite/name_processor/views/tool_audit_tab.py:82 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules..." -msgstr "Yapılandır" +msgstr "Kuralları Yapılandır..." #: NameSuite/name_processor/views/tool_audit_tab.py:87 msgid "Match Pre-Revolutionary Orthography" -msgstr "" +msgstr "Devrim Öncesi Yazım Kurallarıyla Eşleştir" #: NameSuite/name_processor/views/tool_audit_tab.py:95 msgid "Audit Database" -msgstr "" +msgstr "Veritabanı Denetimi" #: NameSuite/name_processor/views/tool_audit_tab.py:117 msgid "Select All Safe Corrections" -msgstr "" +msgstr "Tüm Güvenli Düzeltmeleri Seç" #: NameSuite/name_processor/views/tool_audit_tab.py:122 #: NameSuite/name_processor/views/tool_rename_tab.py:116 -#, fuzzy -#| msgid "Apply to selected places" msgid "Apply Selected Corrections" -msgstr "Seçilen yerlere uygula" +msgstr "Seçilen Düzeltmeleri Uygula" #: NameSuite/name_processor/views/tool_audit_tab.py:162 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules" -msgstr "Yapılandır" +msgstr "Kuralları Yapılandır" #: NameSuite/name_processor/views/tool_audit_tab.py:186 #: NameSuite/name_processor/views/tool_rename_tab.py:156 msgid "Individual" -msgstr "" +msgstr "Birey" #: NameSuite/name_processor/views/tool_audit_tab.py:190 #: NameSuite/name_processor/views/tool_rename_tab.py:158 -#, fuzzy -#| msgid "Current sort" msgid "Current" -msgstr "Mevcut sıralama" +msgstr "Geçerli" #: NameSuite/name_processor/views/tool_audit_tab.py:193 -#, fuzzy -#| msgid "Deep Connections" msgid "Correction" -msgstr "Derin Bağlantılar" +msgstr "Düzeltme" #: NameSuite/name_processor/views/tool_audit_tab.py:200 -#, fuzzy -#| msgid "Configure" msgid "Conf" msgstr "Yapılandır" #: NameSuite/name_processor/views/tool_audit_tab.py:201 -#, fuzzy -#| msgid "Year" msgid "Ref Year" -msgstr "Yıl" +msgstr "Referans Yılı" #: NameSuite/name_processor/views/tool_audit_tab.py:204 -#, fuzzy -#| msgid "Animation" msgid "Explanation" -msgstr "Animasyon" +msgstr "Açıklama" #: NameSuite/name_processor/views/tool_audit_tab.py:243 msgid "Audit Complete!" -msgstr "" +msgstr "Denetim Tamamlandı!" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "Result" msgid "No Results" -msgstr "Sonuç" +msgstr "Sonuç Bulunamadı" #: NameSuite/name_processor/views/tool_audit_tab.py:248 msgid "No issues found." -msgstr "" +msgstr "Herhangi bir sorun bulunamadı." #: NameSuite/name_processor/views/tool_rename_tab.py:63 msgid "Search and Replace Options" -msgstr "" +msgstr "Arama ve Değiştirme Seçenekleri" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Source type" msgid "Source Name:" -msgstr "Kaynak türü" +msgstr "Kaynak Adı:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" -msgstr "" +msgstr "Örneğin John" #: NameSuite/name_processor/views/tool_rename_tab.py:75 msgid "Target Name:" -msgstr "" +msgstr "Hedef Adı:" #: NameSuite/name_processor/views/tool_rename_tab.py:77 msgid "e.g. Иван" -msgstr "" +msgstr "Örneğin Ivan" #: NameSuite/name_processor/views/tool_rename_tab.py:80 msgid "Match Mode:" -msgstr "" +msgstr "Eşleşme Modu:" #: NameSuite/name_processor/views/tool_rename_tab.py:82 msgid "Exact Match" -msgstr "" +msgstr "Tam Eşleşme" #: NameSuite/name_processor/views/tool_rename_tab.py:83 -#, fuzzy -#| msgid "Search substring..." msgid "Substring" -msgstr "Alt dizeyi ara..." +msgstr "Alt dize" #: NameSuite/name_processor/views/tool_rename_tab.py:84 -#, fuzzy -#| msgid "Allow regular expressions." msgid "Regular Expression" -msgstr "Normal ifadelerin kullanılmasına izin verin." +msgstr "Normal İfade" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Index of Names" msgid "Scan for Names" -msgstr "Adlar Dizini" +msgstr "Adlara Göre Tara" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" -msgstr "" +msgstr "Orijinal adı alternatif olarak koruyun" #: NameSuite/name_processor/views/tool_rename_tab.py:160 -#, fuzzy -#| msgid "Proposed sort" msgid "Proposed" -msgstr "Önerilen sıralama" +msgstr "Önerilen" #: NetworkChart/NetworkChart.gpr.py:24 msgid "Network Chart" -msgstr "" +msgstr "Ağ Grafiği" #: NetworkChart/NetworkChart.gpr.py:35 msgid "Generates a family network chart." -msgstr "" +msgstr "Bir aile ağacı grafiği oluşturur." #: NetworkChart/NetworkChart.py:244 msgid "File exists. Overwrite?" -msgstr "" +msgstr "Dosya mevcut. Üzerine yazılsın mı?" #: NetworkChart/NetworkChart.py:898 msgid "created" -msgstr "" +msgstr "oluşturuldu" #: NetworkChart/NetworkChart.py:899 NetworkChart/NetworkChart.py:902 msgid "last_written" -msgstr "" +msgstr "son_yazılan" #: NetworkChart/NetworkChart.py:912 msgid "Main" -msgstr "" +msgstr "Ana" #: NetworkChart/NetworkChart.py:916 msgid "Orthogonal (right angles in connectors)" -msgstr "" +msgstr "Ortogonal (bağlantılarda dik açılar)" #: NetworkChart/NetworkChart.py:917 msgid "Straight (no right angles in connectors)" -msgstr "" +msgstr "Düz (bağlantı noktalarında dik açılar yok)" #: NetworkChart/NetworkChart.py:918 msgid "Curved (curved and straight connectors)" -msgstr "" +msgstr "Kavisli (kavisli ve düz bağlantı elemanları)" #: NetworkChart/NetworkChart.py:920 msgid "Default (Orthogonal)" -msgstr "" +msgstr "Varsayılan (Ortogonal)" #: NetworkChart/NetworkChart.py:925 msgid "Select the graph line connector format." -msgstr "" +msgstr "Grafik çizgi bağlantı biçimini seçin." #: NetworkChart/NetworkChart.py:930 msgid "Left to Right" -msgstr "" +msgstr "Soldan Sağa" #: NetworkChart/NetworkChart.py:930 msgid "Top to Bottom" -msgstr "" +msgstr "Yukarıdan Aşağıya" #: NetworkChart/NetworkChart.py:931 msgid "Bottom to Top" -msgstr "" +msgstr "Aşağıdan Yukarıya" #: NetworkChart/NetworkChart.py:931 msgid "Right to Left" -msgstr "" +msgstr "Sağdan Sola" #: NetworkChart/NetworkChart.py:933 msgid "Default (Top to Bottom)" -msgstr "" +msgstr "Varsayılan (Yukarıdan Aşağıya)" #: NetworkChart/NetworkChart.py:936 msgid "Select the graph direction." -msgstr "" +msgstr "Grafik yönünü seçin." #: NetworkChart/NetworkChart.py:939 msgid "URL Style" -msgstr "" +msgstr "URL Stili" #: NetworkChart/NetworkChart.py:941 msgid "Include URLs from database." -msgstr "" +msgstr "Veritabanından URL adreslerini dahil et." #: NetworkChart/NetworkChart.py:942 msgid "Dynamic URL = Prefix + GrampID + Suffix" -msgstr "" +msgstr "Dinamik URL = Önek + GrampsID + Sonek" #: NetworkChart/NetworkChart.py:943 msgid "Static URL = Prefix" -msgstr "" +msgstr "Statik URL = Önek" #: NetworkChart/NetworkChart.py:944 msgid "Don't include URLs" -msgstr "" +msgstr "URL adreslerini dahil etme" #: NetworkChart/NetworkChart.py:945 msgid "Default (include)" -msgstr "" +msgstr "Varsayılan (dahil et)" #: NetworkChart/NetworkChart.py:948 msgid "Select URL style." -msgstr "" +msgstr "URL stilini seçin." #: NetworkChart/NetworkChart.py:951 msgid "URL Prefix,Suffix" -msgstr "" +msgstr "URL Önek, Sonek" #: NetworkChart/NetworkChart.py:952 msgid "" @@ -18674,10 +18825,13 @@ msgid "" "for dynamically generated URLs.\n" "URL = Prefix + GrampsID + Suffix." msgstr "" +"Dinamik olarak oluşturulan URL adresleri için\n" +"Önek ve Soneki girin (virgül kullanın).\n" +"URL = Önek + GrampsID + Sonek." #: NetworkChart/NetworkChart.py:957 msgid "Enter Font Name" -msgstr "" +msgstr "Yazı Tipi Adını Girin" #: NetworkChart/NetworkChart.py:959 msgid "" @@ -18686,153 +18840,162 @@ msgid "" "White Rabbit can be obtained from\n" "https://www.fontsquirrel.com/fonts/white-rabbit" msgstr "" +"Grafik için birincil yazı tipi stilini girin.\n" +"Zaten yüklü olmalıdır.\n" +"White Rabbit yazı tipi şu adresten edinilebilir:\n" +"https://www.fontsquirrel.com/fonts/white-rabbit" #: NetworkChart/NetworkChart.py:964 msgid "Spacing (inch)" -msgstr "" +msgstr "Aralık (inç)" #: NetworkChart/NetworkChart.py:965 msgid "" "Enter the seperation distance between \"Generations\" in inches (0.1-5.0)." msgstr "" +"\"Nesiller\" arasındaki ayırma mesafesini inç cinsinden girin (0,1-5,0)." #: NetworkChart/NetworkChart.py:969 msgid "Enter Chart Title" -msgstr "" +msgstr "Grafik Başlığını Girin" #: NetworkChart/NetworkChart.py:971 msgid "Set the title of the chart." -msgstr "" +msgstr "Grafiğin başlığını ayarlayın." #: NetworkChart/NetworkChart.py:976 msgid "Default (svg)" -msgstr "" +msgstr "Varsayılan (svg)" #: NetworkChart/NetworkChart.py:979 msgid "" "svg - Scalable Vector Graphics file.\n" "pdf - Adobe Portable Document Format.\n" msgstr "" +"svg - Ölçeklenebilir Vektör Grafikleri dosyası.\n" +"pdf - Adobe Taşınabilir Belge Biçimi.\n" #: NetworkChart/NetworkChart.py:986 msgid "The destination folder for generated files." -msgstr "" +msgstr "Oluşturulan dosyaların hedef klasörü." #: NetworkChart/NetworkChart.py:990 NetworkChart/NetworkChart.py:1280 #: NetworkChart/NetworkChart.py:1288 msgid "network" -msgstr "" +msgstr "ağ" #: NetworkChart/NetworkChart.py:995 msgid "The filename for the generated svg network chart." -msgstr "" +msgstr "Oluşturulan svg ağ grafiğinin dosya adı." #: NetworkChart/NetworkChart.py:1002 msgid "Different node shapes/edges for gender." -msgstr "" +msgstr "Cinsiyete göre farklı düğüm şekilleri/kenarları." #: NetworkChart/NetworkChart.py:1003 msgid "Node differences for gender." -msgstr "" +msgstr "Cinsiyete göre düğüm farklılıkları." #: NetworkChart/NetworkChart.py:1008 msgid "Male Background" -msgstr "" +msgstr "Erkek Arka Planı" #: NetworkChart/NetworkChart.py:1012 msgid "Male Background Alpha" -msgstr "" +msgstr "Erkek Arka Planı Alfa" #: NetworkChart/NetworkChart.py:1014 msgid "Alpha for Male box background (transparent=0, solid=255)." -msgstr "" +msgstr "Erkek kutusu arka planı için alfa değeri (şeffaf=0, dolu=255)." #: NetworkChart/NetworkChart.py:1018 msgid "Male Box Edge" -msgstr "" +msgstr "Erkek Kutusu Kenarı" #: NetworkChart/NetworkChart.py:1019 msgid "RGB-color for Male box edge." -msgstr "" +msgstr "Erkek kutusu kenarı için RGB rengi." #: NetworkChart/NetworkChart.py:1023 msgid "RGB-color for Female box background." -msgstr "" +msgstr "Kadın kutusu arka planı için RGB rengi." # added: #: NetworkChart/NetworkChart.py:1026 msgid "Female Background Alpha" -msgstr "" +msgstr "Kadın Arka Planı Alfa" #: NetworkChart/NetworkChart.py:1028 msgid "Alpha for Female box background (transparent=0, solid=255)." -msgstr "" +msgstr "Kadın kutusu arka planı için alfa değeri (şeffaf=0, dolu=255)." #: NetworkChart/NetworkChart.py:1033 msgid "Female Box Edge" -msgstr "" +msgstr "Kadın Kutusu Kenarı" #: NetworkChart/NetworkChart.py:1034 msgid "RGB-color for Female box edge." -msgstr "" +msgstr "Kadın kutusu kenarı için RGB rengi." #: NetworkChart/NetworkChart.py:1037 msgid "Other Background" -msgstr "" +msgstr "Diğer Arka Plan" #: NetworkChart/NetworkChart.py:1038 msgid "RGB-color for other box background." -msgstr "" +msgstr "Diğer kutu arka planı için RGB rengi." #: NetworkChart/NetworkChart.py:1041 msgid "Other Background Alpha" -msgstr "" +msgstr "Diğer Arka Plan Alfa" #: NetworkChart/NetworkChart.py:1043 msgid "Alpha for Other box background (transparent=0, solid=255)." -msgstr "" +msgstr "Diğer kutu arka planı için alfa değeri (şeffaf=0, dolu=255)." #: NetworkChart/NetworkChart.py:1047 msgid "Family Connector Line" -msgstr "" +msgstr "Aile Bağlantı Çizgisi" #: NetworkChart/NetworkChart.py:1048 msgid "RGB-color for family connector line." -msgstr "" +msgstr "Aile bağlantı çizgisi için RGB rengi." #: NetworkChart/NetworkChart.py:1051 msgid "Marriage Connector Line" -msgstr "" +msgstr "Evlilik Bağlantı Çizgisi" #: NetworkChart/NetworkChart.py:1052 msgid "RGB-color for marriage connector line." -msgstr "" +msgstr "Evlilik bağlantı çizgisi için RGB rengi." #: NetworkChart/NetworkChart.py:1055 msgid "Highlight Connector Line" -msgstr "" +msgstr "Vurgulanmış Bağlantı Çizgisi" #: NetworkChart/NetworkChart.py:1056 msgid "" "RGB-color for the highlighted path between two individuals. If the " "graph.inverts, reverse the order to change." msgstr "" +"İki kişi arasındaki vurgulanmış yol için RGB rengi. Grafik tersine " +"çevrilirse, değiştirmek için sırayı tersine çevirin." #: NetworkChart/NetworkChart.py:1061 msgid "Trim Box Edge" -msgstr "" +msgstr "Kırpılmış Kutu Kenarı" #: NetworkChart/NetworkChart.py:1062 msgid "RGB-color for box edge that has been trimmed." -msgstr "" +msgstr "Kırpılmış kutu kenarı için RGB rengi." #: NetworkChart/NetworkChart.py:1066 msgid "Remove color background on all nodes." -msgstr "" +msgstr "Tüm düğümlerdeki renk arka planını kaldırın." #: NetworkChart/NetworkChart.py:1068 msgid "Removes color background for individuals (nodes) on chart." -msgstr "" +msgstr "Grafikteki bireyler (düğümler) için renkli arka planı kaldırır." #: NetworkChart/NetworkChart.py:1073 msgid "" @@ -18840,20 +19003,25 @@ msgid "" "rounding birthday\n" "to year only" msgstr "" +"Doğum gününü yalnızca\n" +"yıla yuvarlamaya başlamak\n" +"için yılı girin" #: NetworkChart/NetworkChart.py:1076 msgid "" "Birthdays after this year are represented by the year only. Invalid entries " "will be ignored." msgstr "" +"Bu yıldan sonraki doğum günleri yalnızca yıl ile temsil edilir. Geçersiz " +"girişler yok sayılacaktır." #: NetworkChart/NetworkChart.py:1080 msgid "Round birthday to year after year entered (above)." -msgstr "" +msgstr "Girilen yıldan sonraki doğum gününü yıla yuvarlayın (yukarıda)." #: NetworkChart/NetworkChart.py:1082 msgid "Represent birthday by year only." -msgstr "" +msgstr "Doğum gününü yalnızca yıl olarak göster." #: NetworkChart/NetworkChart.py:1086 msgid "" @@ -18861,92 +19029,106 @@ msgid "" "rounding marriage\n" "date to year only" msgstr "" +"Evlilik tarihinin yalnızca\n" +"yıla yuvarlanmaya\n" +"başlanması için yılı girin" #: NetworkChart/NetworkChart.py:1087 msgid "" "Marriages after this year are represented bythe year only. Invalid entries " "will be ignored." msgstr "" +"Bu yıldan sonraki evlilikler yalnızca yıl ile temsil edilir. Geçersiz " +"girişler yok sayılacaktır." #: NetworkChart/NetworkChart.py:1092 msgid "Round marriage to year after year entered (above)." -msgstr "" +msgstr "Girilen yıla göre evlilik yılını yuvarla (yukarıda)." #: NetworkChart/NetworkChart.py:1094 msgid "Represent marriage by year only." -msgstr "" +msgstr "Evliliği yalnızca yıla göre gösterin." #: NetworkChart/NetworkChart.py:1099 msgid "" "Attempts to remove middle names. Only works for entries with birth year in " "the record." msgstr "" +"İkinci adları kaldırmayı dener. Yalnızca kayıtta doğum yılı bulunan " +"girdilerde çalışır." #: NetworkChart/NetworkChart.py:1107 msgid "Remove middle names." -msgstr "" +msgstr "İkinci adları kaldır." #: NetworkChart/NetworkChart.py:1112 msgid "Allow inclusion of private records in chart." -msgstr "" +msgstr "Grafiğe özel kayıtların dahil edilmesine izin ver." #: NetworkChart/NetworkChart.py:1117 msgid "Trim" -msgstr "" +msgstr "Kırp" #: NetworkChart/NetworkChart.py:1119 msgid "Trim descendants" -msgstr "" +msgstr "Soyundan gelenleri kırp" #: NetworkChart/NetworkChart.py:1121 msgid "All descendants (children) of selected individuals will not be shown." msgstr "" +"Seçilen kişilerin tüm soyundan gelenleri (çocukları) gösterilmeyecektir." #: NetworkChart/NetworkChart.py:1127 msgid "Enable trimming of descendants." -msgstr "" +msgstr "Soyundan gelenlerin kırpılmasını etkinleştir." #: NetworkChart/NetworkChart.py:1129 msgid "" "You must enter valid person(s) before \n" "enabling trimming of descendants from tree." msgstr "" +"Ağaçta soyundan gelenlerin kırpılmasını\n" +"etkinleştirmeden önce geçerli kişi(leri) girmelisiniz." #: NetworkChart/NetworkChart.py:1134 msgid "Trim ancestors" -msgstr "" +msgstr "Ataları kırpın" #: NetworkChart/NetworkChart.py:1135 msgid "All ancestors (parents) of selected individuals will not be shown." -msgstr "" +msgstr "Seçilen kişilerin tüm ataları (ebeveynleri) gösterilmeyecektir." #: NetworkChart/NetworkChart.py:1141 msgid "Enable trimming of ancestors." -msgstr "" +msgstr "Ataların kırpılmasını etkinleştir." #: NetworkChart/NetworkChart.py:1143 msgid "Enable trimming of ancestors from tree." -msgstr "" +msgstr "Ağaçtan ataların kırpılmasını etkinleştir." #: NetworkChart/NetworkChart.py:1147 msgid "Enable trim groups." -msgstr "" +msgstr "Grupları kırpmayı etkinleştirin." #: NetworkChart/NetworkChart.py:1149 msgid "" "Remove groups less than Min Group Size. Automatic\n" "for databases with more than 1500 individuals." msgstr "" +"En Küçük Grup Boyutu değerinden küçük grupları kaldır.\n" +"1500'den fazla kişi içeren veritabanları için otomatik olarak uygulanır." #: NetworkChart/NetworkChart.py:1154 msgid "Min Group Size" -msgstr "" +msgstr "En Küçük Grup Boyutu" #: NetworkChart/NetworkChart.py:1156 msgid "" "Enter the minimum size group to display.\n" "Value may be 2 or greater." msgstr "" +"Gösterilecek en küçük grup boyutunu girin.\n" +"Değer 2 veya daha büyük olabilir." #: NetworkChart/NetworkChart.py:1164 msgid "" @@ -18955,24 +19137,30 @@ msgid "" "displayed path(s).\n" "Select two" msgstr "" +"Görüntülenen yol(lar)daki\n" +"başlangıç ve bitiş\n" +"kişilerini seçin.\n" +"İki kişi seçin" #: NetworkChart/NetworkChart.py:1167 msgid "" "Starting and ending person for displayed path(s).\n" "Select two people" msgstr "" +"Görüntülenen yol(lar) için başlangıç ve bitiş kişisi.\n" +"İki kişi seçin" #: NetworkChart/NetworkChart.py:1171 msgid "Highlight path(s)" -msgstr "" +msgstr "Yol(lar)ı vurgula" #: NetworkChart/NetworkChart.py:1174 NetworkChart/NetworkChart.py:1188 msgid "Any" -msgstr "" +msgstr "Herhangi bir" #: NetworkChart/NetworkChart.py:1176 msgid "Default (None)" -msgstr "" +msgstr "Varsayılan (Yok)" #: NetworkChart/NetworkChart.py:1181 msgid "" @@ -18980,14 +19168,18 @@ msgid "" "Direct - Highlight direct descendant/ancestor paths.\n" "Any - Highlight any path(s) including direct or indirect." msgstr "" +"Yok - Yolları vurgulamayın.\n" +"Doğrudan - Doğrudan soyundan gelen/ata yollarını vurgula.\n" +"Herhangi bir - Doğrudan veya dolaylı dahil olmak üzere herhangi bir yolu " +"vurgula." #: NetworkChart/NetworkChart.py:1186 msgid "Show only path(s)" -msgstr "" +msgstr "Yalnızca yol(lar)ı göster" #: NetworkChart/NetworkChart.py:1189 msgid "Default (none)" -msgstr "" +msgstr "Varsayılan (yok)" #: NetworkChart/NetworkChart.py:1193 msgid "" @@ -18995,100 +19187,113 @@ msgid "" "Direct - Show only direct descendant/ancestor paths.\n" "Any - Show only path(s) direct or indirect." msgstr "" +"Yok - Yalnızca yolları gösterme.\n" +"Doğrudan - Yalnızca doğrudan soyundan gelen/ata yollarını göster.\n" +"Herhangi bir - Yalnızca doğrudan veya dolaylı yol(lar)ı göster." #: NetworkChart/NetworkChart.py:1200 msgid "Select person at center of selection radius in graph." -msgstr "" +msgstr "Grafikteki seçim yarıçapının merkezindeki kişiyi seçin." #: NetworkChart/NetworkChart.py:1203 msgid "" "Max connections\n" "from center" msgstr "" +"Merkezden en\n" +"fazla bağlantı" #: NetworkChart/NetworkChart.py:1206 msgid "" "Enter the maximum number of connections allowed from\n" "the center person. Value may be 1 or greater." msgstr "" +"Merkez kişiden izin verilen en fazla bağlantı sayısını girin.\n" +"Değer 1 veya daha büyük olabilir." #: NetworkChart/NetworkChart.py:1211 msgid "Limit graph to max connections from center." -msgstr "" +msgstr "Grafiği merkezden en fazla bağlantı sayısıyla sınırla." #: NetworkChart/NetworkChart.py:1213 msgid "Display up to max connections from central person." -msgstr "" +msgstr "Merkez kişiden en fazla bağlantı sayısına kadar görüntüle." #: NetworkChart/NetworkChart.py:1217 msgid "Highlight central person in graph." -msgstr "" +msgstr "Grafikte merkez kişiyi vurgula." #: NetworkChart/NetworkChart.py:1219 msgid "Add yellow color background to central person." -msgstr "" +msgstr "Merkez kişiye sarı arka plan rengi ekle." #: NetworkChart/NetworkChart.py:1234 msgid "Use database handle instead of GrampID id in URLs." msgstr "" +"URL adreslerinde GrampsID kimliği yerine veritabanı tanıtıcısını (handle) " +"kullan." #: NetworkChart/NetworkChart.py:1236 msgid "" "Use database handle instead of the gramps id for URLs.\n" "The handle should never change whereas gramps ids can." msgstr "" +"URL adrslerinde Gramps kimliği yerine veritabanı tanıtıcısını (handle) " +"kullan.\n" +"Tanıtıcı (handle) hiçbir zaman değişmemelidir; oysa Gramps kimlikleri " +"değişebilir." #: NetworkChart/NetworkChart.py:1240 msgid "Confirm overwrite file." -msgstr "" +msgstr "Dosyanın üzerine yazmayı onayla." #: NetworkChart/NetworkChart.py:1243 msgid "Enable/disable confirmation of file overwrite." -msgstr "" +msgstr "Dosyanın üzerine yazma onayını etkinleştir/devre dışı bırak." #: NoteCleanup/NoteCleanup.gpr.py:31 NoteCleanup/NoteCleanup.py:412 msgid "Note Cleanup" -msgstr "" +msgstr "Not Temizleme" #: NoteCleanup/NoteCleanup.gpr.py:32 msgid "Clean up Notes that contain HTML markup" -msgstr "" +msgstr "HTML işaretlemesi içeren notları temizle" #: NoteCleanup/NoteCleanup.py:92 msgid "Note Cleanup Tool" -msgstr "" +msgstr "Not Temizleme Aracı" #: NoteCleanup/NoteCleanup.py:98 msgid "Cleaned Notes" -msgstr "" +msgstr "Temizlenmiş Notlar" #: NoteCleanup/NoteCleanup.py:98 msgid "Links Only" -msgstr "" +msgstr "Yalnızca Bağlantılar" #: NoteCleanup/NoteCleanup.py:99 msgid "Issues" -msgstr "" +msgstr "Sorunlar" #: NoteCleanup/NoteCleanup.py:121 msgid "Close the Note Cleanup Tool" -msgstr "" +msgstr "Not Temizleme Aracını Kapat" #: NoteCleanup/NoteCleanup.py:123 msgid "Save All" -msgstr "" +msgstr "Tümünü Kaydet" #: NoteCleanup/NoteCleanup.py:124 msgid "Save All Changes" -msgstr "" +msgstr "Tüm Değişiklikleri Kaydet" #: NoteCleanup/NoteCleanup.py:127 msgid "Search for Untidy Notes" -msgstr "" +msgstr "Düzensiz Notları Ara" #: NoteCleanup/NoteCleanup.py:129 msgid "Generate Test Notes" -msgstr "" +msgstr "Test Notları Oluştur" #: NoteCleanup/NoteCleanup.py:131 msgid "" @@ -19096,6 +19301,9 @@ msgid "" "These are added to your database, so you may want to work with a test " "database or delete them later." msgstr "" +"N99996-N99999 aralığında test notları oluştur.\n" +"Bunlar veritabanınıza eklenecektir; bu nedenle bir test veritabanı kullanmak " +"veya daha sonra bunları silmek isteyebilirsiniz." #: NoteCleanup/NoteCleanup.py:181 msgid "" @@ -19106,18 +19314,26 @@ msgid "" "You may export a summary list of the notes that were found using the " "'Export' button." msgstr "" +"Lütfen bu aracı çalıştırmadan önce veritabanınızın yedeğini alın.\n" +"\n" +"Aracı başlatmak için \"Ara\" düğmesine basın, ardından sonuçları gözden " +"geçirin.\n" +"Memnun kaldığınızda çalışmanızı kaydetmek için \"Tümünü Kaydet\" düğmesine " +"basın.\n" +"Bulunan notların özet listesini \"Dışa Aktar\" düğmesini kullanarak dışa " +"aktarabilirsiniz." #: NoteCleanup/NoteCleanup.py:190 msgid "Clean up Notes" -msgstr "" +msgstr "Notları Temizle" #: NoteCleanup/NoteCleanup.py:288 msgid "Cleanup Test Notes" -msgstr "" +msgstr "Test Notlarını Temizle" #: NoteCleanup/NoteCleanup.py:310 NoteCleanup/NoteCleanup.py:315 msgid "Add Test Note" -msgstr "" +msgstr "Test Notu Ekle" #: NoteCleanup/NoteCleanup.py:383 msgid "" @@ -19125,6 +19341,9 @@ msgid "" "\n" "Notes selected on the left pane are shown Before cleanup in this box." msgstr "" +"\n" +"\n" +"Sol bölmede seçilen notlar, temizlemeden önce bu kutuda gösterilir." #: NoteCleanup/NoteCleanup.py:386 msgid "" @@ -19134,55 +19353,60 @@ msgid "" "If you wish to make changes, you can make them here and use the style " "controls in the toolbar above." msgstr "" +"\n" +"\n" +"Sol bölmede seçilen notlar, temizlemeden sonra bu kutuda gösterilir.\n" +"Değişiklik yapmak isterseniz, burada değişiklik yapabilir ve yukarıdaki araç " +"çubuğundaki stil kontrollerini kullanabilirsiniz." #: NoteCleanup/NoteCleanup.py:404 msgid "Saving Notes" -msgstr "" +msgstr "Notları Kaydetme" #: NoteCleanup/NoteCleanup.py:407 msgid "Saving Cleaned Notes" -msgstr "" +msgstr "Temizlenmiş Notları Kaydetme" #: NoteCleanup/NoteCleanup.py:435 msgid "Scanning Notes" -msgstr "" +msgstr "Notları Tarama" #: NoteGramplet/NoteGramplet.gpr.py:4 msgid "Note Gramplet" -msgstr "" +msgstr "Not Grampleti" #: NoteGramplet/NoteGramplet.gpr.py:5 msgid "Gramplet for editing active person's notes" -msgstr "" +msgstr "Etkin kişinin notlarını düzenlemek için Gramplet" #: NoteGramplet/NoteGramplet.py:255 msgid "Save Note" -msgstr "" +msgstr "Notu Kaydet" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.gpr.py:4 msgid "Number of ancestors" -msgstr "" +msgstr "Ataların Sayısı" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.gpr.py:5 msgid "Shows the number of ancestors of the current person" -msgstr "" +msgstr "Geçerli kişinin atalarının sayısını gösterir" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:50 #, python-format msgid "Number of %s's ancestors" -msgstr "" +msgstr "%s öğesinin atalarının sayısı" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:55 msgid "Found" -msgstr "" +msgstr "Bulunan" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:56 msgid "Theoretical" -msgstr "" +msgstr "Teorik" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:57 msgid "Percent" -msgstr "" +msgstr "Yüzde" #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:65 msgid "" @@ -19190,173 +19414,187 @@ msgid "" "Only individual ancestors were counted. Duplicates caused by pedigree " "collapse were ignored." msgstr "" +"{} atadan {} tanesi ({}) bulundu.\n" +"Yalnızca tekil atalar sayıldı. Soy ağacındaki çakışmalardan kaynaklanan " +"yinelenenler göz ardı edildi." #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.gpr.py:5 msgid "Shows the number of descendants of the current person" -msgstr "" +msgstr "Mevcut kişinin soyundan gelenlerin sayısını gösterir" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:62 #, python-format msgid "Number of %s's descendants" -msgstr "" +msgstr "%s öğesinin soyundan gelenlerinin sayısı" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:89 msgid "Seen" -msgstr "" +msgstr "Görülen" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:90 msgid "Outlived" -msgstr "" +msgstr "Ömrü boyunca hayatta kalan" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:91 #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:96 msgid "Now alive" -msgstr "" +msgstr "Şu anda hayatta olan" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:122 #, python-format msgid "Seen = number of descendants whose birth %s has lived to see" -msgstr "" +msgstr "Görülen = %s öğesinin doğumunu gördüğü soyundan gelenlerin sayısı" #: NumberOfDescendantsQuickview/NumberOfDescendantsQuickview.py:124 #, python-format msgid "Outlived = number of descendants who died while %s was still alive" msgstr "" +"Ömrü boyunca hayatta kalanlar = %s hayattayken ölen soyundan gelenlerin " +"sayısı" #: Overview/Overview.gpr.py:29 msgid "Person Overview" -msgstr "" +msgstr "Kişi Genel Bakışı" #: Overview/Overview.gpr.py:30 msgid "Gramplet showing an overview of events for a person" -msgstr "" +msgstr "Bir kişi için olayların genel görünümünü gösteren Gramplet" #: Overview/Overview.gpr.py:45 msgid "Family Overview" -msgstr "" +msgstr "Aile Genel Bakışı" #: Overview/Overview.gpr.py:46 msgid "Gramplet showing an overview of events for a family" -msgstr "" +msgstr "Bir aile için olayların genel görünümünü gösteren Gramplet" #: PDFForms/PDFForms.gpr.py:24 PDFForms/generatepdfform.py:57 #: PDFForms/generatepdfform.py:63 msgid "Generate PDF Forms" -msgstr "" +msgstr "PDF Formları Oluştur" #: PDFForms/PDFForms.gpr.py:26 msgid "" "Generate blank fillable PDF forms: census/event forms or Ahnentafel pedigree " "charts." msgstr "" +"Boş doldurulabilir PDF formları oluşturun: nüfus sayımı/olay formları veya " +"Ahnentafel soyağacı çizelgeleri." #: PDFForms/PDFForms.gpr.py:46 msgid "Import Fillable PDF Forms" -msgstr "" +msgstr "Doldurulabilir PDF Formlarını İçe Aktar" #: PDFForms/PDFForms.gpr.py:48 msgid "" "Import genealogy data from a PDF form. Send the PDF template to others to " "fill out and return." msgstr "" +"PDF formundan soyağacı verilerini içe aktarın. PDF şablonunu doldurmaları ve " +"geri göndermeleri için başkalarına gönderin." #: PDFForms/generatepdfform.py:77 msgid "Census / Event Form" -msgstr "" +msgstr "Nüfus Sayımı / Etkinlik Formu" #: PDFForms/generatepdfform.py:79 msgid "Ahnentafel Pedigree Chart" -msgstr "" +msgstr "Ahnentafel Soy Ağacı Grafiği" #: PDFForms/generatepdfform.py:99 msgid "Output file:" -msgstr "" +msgstr "Çıktı dosyası:" #: PDFForms/generatepdfform.py:107 msgid "Browse…" -msgstr "" +msgstr "Gözat…" #: PDFForms/generatepdfform.py:116 msgid "Open PDF after generating" -msgstr "" +msgstr "Oluşturduktan sonra PDF dosyasını aç" #: PDFForms/generatepdfform.py:157 msgid "Form:" -msgstr "" +msgstr "Form:" #: PDFForms/generatepdfform.py:174 msgid "Rows:" -msgstr "" +msgstr "Satırlar:" #: PDFForms/generatepdfform.py:186 msgid "(Rows only affect multi-row sections such as census household lists.)" msgstr "" +"(Satırlar yalnızca nüfus sayımı hane listeleri gibi çok satırlı bölümleri " +"etkiler.)" #: PDFForms/generatepdfform.py:200 msgid "Generations:" -msgstr "Nesil:" +msgstr "Nesiller:" #: PDFForms/generatepdfform.py:213 msgid "Includes subject plus up to 5 ancestor generations (2–32 people)." -msgstr "" +msgstr "Konu ve en fazla 5 ata neslini (2-32 kişi) içerir." #: PDFForms/generatepdfform.py:252 msgid "Save PDF as…" -msgstr "" +msgstr "PDF'yi farklı kaydet…" #: PDFForms/generatepdfform.py:263 msgid "PDF files" -msgstr "" +msgstr "PDF dosyaları" #: PDFForms/generatepdfform.py:283 msgid "No output file" -msgstr "" +msgstr "Çıktı dosyası yok" #: PDFForms/generatepdfform.py:283 msgid "Please specify an output file path." -msgstr "" +msgstr "Lütfen bir çıktı dosyası yolu belirtin." #: PDFForms/generatepdfform.py:293 msgid "No form selected" -msgstr "" +msgstr "Form seçilmedi" #: PDFForms/generatepdfform.py:293 msgid "Please select a census form." -msgstr "" +msgstr "Lütfen bir nüfus sayımı formu seçin." #: PDFForms/generatepdfform.py:299 msgid "Form not found" -msgstr "" +msgstr "Form bulunamadı" #: PDFForms/generatepdfform.py:306 msgid "Generation failed" -msgstr "Nesil başarısız oldu" +msgstr "Nesil oluşturma başarısız oldu" #: PDFForms/generatepdfform.py:317 msgid "PDF generated" -msgstr "" +msgstr "PDF oluşturuldu" #: PDFForms/importformpdf.py:180 msgid "Form addon required" -msgstr "" +msgstr "Form eklentisi gerekli" #: PDFForms/importformpdf.py:181 msgid "" "The Form addon must be installed to import Form PDF files.\n" "Please install the Form addon and restart Gramps." msgstr "" +"Form PDF dosyalarını içe aktarmak için Form eklentisinin yüklü olması " +"gerekir.\n" +"Lütfen Form eklentisini yükleyin ve Gramps’ı yeniden başlatın." #: PDFForms/importformpdf.py:189 msgid "Not a Form PDF" -msgstr "" +msgstr "Form PDF değil" #: PDFForms/importformpdf.py:190 msgid "The _form_id field is missing or empty." -msgstr "" +msgstr "_form_id alanı eksik veya boş." #: PDFForms/importformpdf.py:197 msgid "Unknown form ID" -msgstr "" +msgstr "Bilinmeyen form kimliği" #: PDFForms/importformpdf.py:198 #, python-format @@ -19364,30 +19602,34 @@ msgid "" "Form ID '%(id)s' is not recognised by the Form addon.\n" "Known IDs: %(known)s" msgstr "" +"‘%(id)s’ form kimliği, Form eklentisi tarafından tanınmıyor.\n" +"Bilinen kimlikler: %(known)s" #: PDFForms/importformpdf.py:221 #, python-format msgid "Import Form PDF: %s" -msgstr "" +msgstr "Form PDF dosyasını içe aktar: %s" #: PDFForms/importpdf.py:187 msgid "" "The pypdf package is required to import PDF pedigree forms.\n" "Install it with: pip install pypdf" msgstr "" +"PDF soy ağacı formlarını içe aktarmak için pypdf paketi gereklidir.\n" +"Şu komutla yükleyin: pip install pypdf" #: PDFForms/importpdf.py:195 #, python-format msgid "Could not open PDF file: %s" -msgstr "" +msgstr "PDF dosyası açılamadı: %s" #: PDFForms/importpdf.py:399 msgid "Add media reference: PDF import" -msgstr "" +msgstr "Medya referansı ekle: PDF içe aktarma" #: PDFForms/importpdf.py:448 msgid "PDF import: no data found" -msgstr "" +msgstr "PDF içe aktarma: veri bulunamadı" #: PDFForms/importpdf.py:449 msgid "" @@ -19395,26 +19637,29 @@ msgid "" "\n" "Fill in the form fields and try again." msgstr "" +"Soy ağacı formunda hiçbir ad bulunamadı.\n" +"\n" +"Form alanlarını doldurun ve tekrar deneyin." #: PDFForms/importpdf.py:471 msgid "Bad references in PDF import" -msgstr "" +msgstr "PDF içe aktarmada hatalı referanslar" #: PDFForms/importpdf.py:492 msgid "PDF import error" -msgstr "" +msgstr "PDF içe aktarma hatası" #: PDFForms/importpdf.py:503 msgid "Form importer unavailable" -msgstr "" +msgstr "Form içe aktarıcısı kullanılamıyor" #: PDFForms/importpdf.py:504 msgid "importformpdf.py is missing from the PDFForms addon directory." -msgstr "" +msgstr "importformpdf.py, PDFForms eklenti dizininde eksik." #: PDFForms/importpdf.py:510 msgid "PDF import: unrecognized format" -msgstr "" +msgstr "PDF içe aktarma: tanınmayan biçim" #: PDFForms/importpdf.py:511 #, python-format @@ -19423,115 +19668,123 @@ msgid "" "\n" "Only PDFs generated by Gramps (pedigree or form templates) can be imported." msgstr "" +"'%s' içinde Gramps form kimliği bulunamadı.\n" +"\n" +"Yalnızca Gramps tarafından oluşturulan PDF dosyaları (soyağacı veya form " +"şablonları) içe aktarılabilir." #: Participants/Participants.gpr.py:32 msgid "Gramplet showing the participants in an event" -msgstr "" +msgstr "Bir etkinliğe katılanları gösteren Gramplet" #: Participants/Participants.py:75 msgid "Double-click on a row to edit the selected participant." -msgstr "" +msgstr "Seçilen katılımcıyı düzenlemek için bir satıra çift tıklayın." #: PedigreeChart/PedigreeChart.gpr.py:32 msgid "Pedigree Chart" -msgstr "" +msgstr "Soyağacı Grafiği" #: PedigreeChart/PedigreeChart.gpr.py:33 msgid "Alternate version of the traditional pedigree chart." -msgstr "" +msgstr "Geleneksel soyağacı grafiğinin alternatif sürümü." #: PedigreeChart/PedigreeChart.py:391 #, python-format msgid "Pedigree Chart for %s" -msgstr "" +msgstr "%s için Soyağacı Grafiği" #: PedigreeChart/PedigreeChart.py:585 #, python-format msgid "Page %d" -msgstr "" +msgstr "Sayfa %d" #: PedigreeChart/PedigreeChart.py:714 msgid "Show Mother/Father captions" -msgstr "" +msgstr "Anne/Baba unvanlarını göster" #: PedigreeChart/PedigreeChart.py:715 msgid "Show the title of mother or father beside each ancestor's box." -msgstr "" +msgstr "Her atanın kutusunun yanında anne veya babanın unvanını göster." #: PedigreeChart/PedigreeChart.py:718 msgid "Show page numbers" -msgstr "" +msgstr "Sayfa numaralarını göster" #: PedigreeChart/PedigreeChart.py:719 msgid "Add a footer on every page with the page number and date printed." msgstr "" +"Her sayfaya sayfa numarası ve yazdırılan tarihle birlikte bir altbilgi " +"ekleyin." #: PedigreeChart/PedigreeChart.py:740 msgid "The basic style used for the title display." -msgstr "" +msgstr "Başlık gösterimi için kullanılan temel stil." #: PedigreeChart/PedigreeChart.py:749 msgid "Style used for names (only for PDF document and PostScript)." -msgstr "" +msgstr "Adlar için kullanılan stil (yalnızca PDF belgesi ve PostScript için)." #: PedigreeChart/PedigreeChart.py:758 msgid "Style used for labels and captions." -msgstr "" +msgstr "Etiketler ve başlıklar için kullanılan stil." #: PersonEverything/PersonEverything.gpr.py:32 msgid "PersonEverything Report" -msgstr "" +msgstr "Kişi Her Şey Raporu" #: PersonEverything/PersonEverything.gpr.py:33 msgid "Produces a report containing everything about the active person" -msgstr "" +msgstr "Etkin kişi hakkında her şeyi içeren bir rapor oluşturur" #: PersonEverything/PersonEverything.py:134 #, python-format msgid "All information about %s" -msgstr "" +msgstr "%s hakkında tüm bilgiler" #: PersonEverything/PersonEverything.py:174 msgid "Primary name" -msgstr "" +msgstr "Birincil ad" #: PersonEverything/PersonEverything.py:181 msgid "Alternate name" -msgstr "" +msgstr "Alternatif ad" #: PersonEverything/PersonEverything.py:197 msgid " This is the primary birth event." -msgstr "" +msgstr " Bu, birincil doğum olayıdır." #: PersonEverything/PersonEverything.py:200 msgid " This is the primary death event." -msgstr "" +msgstr " Bu, birincil ölüm olayıdır." #: PersonEverything/PersonEverything.py:239 msgid "Parent Family" -msgstr "" +msgstr "Ebeveyn Ailesi" #: PersonEverything/PersonEverything.py:241 msgid "" "Details of any children and events etc. would be in a similar report for the " "father or mother." msgstr "" +"Herhangi bir çocuğun ve olayın ayrıntıları vb. baba veya anne için benzer " +"bir raporda yer alacaktır." #: PersonEverything/PersonEverything.py:404 msgid "Temple and status" -msgstr "" +msgstr "Tapınak ve durumu" #: PersonEverything/PersonEverything.py:415 msgid "LDS Ordinance family" -msgstr "" +msgstr "LDS Yönetmeliği ailesi" #: PersonEverything/PersonEverything.py:419 msgid "LDS " -msgstr "" +msgstr "LDS " #: PersonEverything/PersonEverything.py:435 msgid "Street, City, County, State, Postal Code, Country, Phone number" -msgstr "" +msgstr "Sokak, Şehir, İlçe, Eyalet, Posta Kodu, Ülke, Telefon numarası" #: PersonEverything/PersonEverything.py:449 msgid "Media Reference" @@ -19540,315 +19793,319 @@ msgstr "Ortam referansı" #: PersonEverything/PersonEverything.py:456 #: PersonEverything/PersonEverything.py:733 msgid "Description and Path" -msgstr "" +msgstr "Açıklama ve Yol" #: PersonEverything/PersonEverything.py:485 msgid "Referenced Region" -msgstr "" +msgstr "Referans Bölge" #: PersonEverything/PersonEverything.py:506 msgid "Mime type" -msgstr "" +msgstr "Mime türü" #: PersonEverything/PersonEverything.py:517 msgid "" "Given name(s): Title, Given, Suffix, Call Name, Nick Name, Family Nick Name" msgstr "" +"Verilen ad(lar): Unvan, Verilen Ad, Ek, Çağrı Adı, Takma Ad, Aile Takma Adı" #: PersonEverything/PersonEverything.py:543 msgid "Note type" -msgstr "" +msgstr "Not türü" #: PersonEverything/PersonEverything.py:554 msgid "Parent Place" -msgstr "" +msgstr "Ana Konum" #: PersonEverything/PersonEverything.py:569 #: PersonEverything/PersonEverything.py:573 msgid "Alternate Location" -msgstr "" +msgstr "Alternatif Konum" #: PersonEverything/PersonEverything.py:608 msgid "Alternative Name" -msgstr "" +msgstr "Alternatif Ad" #: PersonEverything/PersonEverything.py:619 msgid "Latitude, Longitude" -msgstr "" +msgstr "Enlem, Boylam" #: PersonEverything/PersonEverything.py:649 msgid "Repository type" -msgstr "" +msgstr "Depo türü" #: PersonEverything/PersonEverything.py:671 msgid "Repository reference" -msgstr "" +msgstr "Depo referansı" #: PersonEverything/PersonEverything.py:672 msgid "Media type" -msgstr "" +msgstr "Medya türü" #: PersonEverything/PersonEverything.py:680 msgid "Origin type" -msgstr "" +msgstr "Kaynak türü" #: PersonEverything/PersonEverything.py:687 msgid "Prefix, surname, connector" -msgstr "" +msgstr "Ön ek, soyadı, bağlayıcı" #: PersonEverything/PersonEverything.py:692 msgid "{This is the primary surname}" -msgstr "" +msgstr "{Bu birincil soyadıdır}" #: PersonEverything/PersonEverything.py:705 msgid "Tag name" -msgstr "" +msgstr "Etiket adı" #: PersonEverything/PersonEverything.py:716 msgid "Tag colour and priority" -msgstr "" +msgstr "Etiket rengi ve önceliği" #: PersonEverything/PersonEverything.py:961 msgid "No source information found" -msgstr "" +msgstr "Kaynak bilgisi bulunamadı" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" -msgstr "" +msgstr "Fotoğraf Etiketleme" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:30 msgid "Gramplet for tagging people in photos" -msgstr "" +msgstr "Fotoğraflardaki kişileri etiketlemek için Gramplet" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:114 msgid "https://www.gramps-project.org/wiki/index.php/Photo_Tagging_Gramplet" -msgstr "" +msgstr "https://www.gramps-project.org/wiki/index.php/Photo_Tagging_Gramplet" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:140 msgid "Replace existing references to the person being assigned without asking" -msgstr "" +msgstr "Atama yapılan kişiye ait mevcut referansları sormadan değiştir" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:147 msgid "Face detection" -msgstr "" +msgstr "Yüz algılama" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:150 msgid "Minimum face width (px)" -msgstr "" +msgstr "Minimum yüz genişliği (px)" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:152 msgid "Minimum face height (px)" -msgstr "" +msgstr "Minimum yüz yüksekliği (px)" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:155 msgid "Detect faces inside existing boxes" -msgstr "" +msgstr "Mevcut kutuların içindeki yüzleri algıla" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:157 msgid "Sensitivity (1 min .. 20 max)" -msgstr "" +msgstr "Hassasiyet (min. 1 .. maks. 20)" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:297 msgid "Add Person" -msgstr "" +msgstr "Kişi Ekle" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:298 msgid "Clear Reference" -msgstr "" +msgstr "Referansı Temizle" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:299 msgid "Remove Selection" -msgstr "" +msgstr "Seçimi Kaldır" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:300 msgid "Edit referenced Person" -msgstr "" +msgstr "Referans verilen Kişiyi düzenle" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:301 msgid "Zoom In" -msgstr "" +msgstr "Yakınlaştır" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:302 msgid "Zoom Out" -msgstr "" +msgstr "Uzaklaştır" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:305 msgid "Detect faces" -msgstr "" +msgstr "Yüzleri algıla" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:307 msgid "Detect faces (OpenCV module required)" -msgstr "" +msgstr "Yüzleri algıla (OpenCV modülü gereklidir)" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:362 msgid "XMP Region Name" -msgstr "" +msgstr "XMP Bölge Adı" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:501 msgid "Set as active person" -msgstr "" +msgstr "Etkin kişi olarak ayarla" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:504 RelID/relation_tab.py:633 msgid "_Select" -msgstr "" +msgstr "_Seç" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:785 #, python-brace-format msgid "Replace to {0}" -msgstr "" +msgstr "{0} ile değiştir" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:886 msgid "Detecting faces..." -msgstr "" +msgstr "Yüzler algılanıyor..." #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:911 msgid "Detection finished" -msgstr "" +msgstr "Algılama tamamlandı" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:961 #, python-brace-format msgid "Another region of this image is associated with {name}. Remove it?" msgstr "" +"Bu görüntünün başka bir bölgesi {name} ile ilişkilendirilmiş. Kaldırılsın mı?" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:964 #, python-brace-format msgid "" "{count} other regions of this image are associated with {name}. Remove them?" msgstr "" +"Bu görüntünün {count} diğer bölgesi {name} ile ilişkilendirilmiş. " +"Kaldırılsın mı?" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:1116 #, python-format msgid "Male person|Dead at %s" -msgstr "" +msgstr "Erkek kişi|%s tarihinde öldü" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:1118 #, python-format msgid "Female person|Dead at %s" -msgstr "" +msgstr "Kadın kişi|%s tarihinde öldü" #: PhotoTaggingGramplet/PhotoTaggingGramplet.py:1120 #, python-format msgid "Unknown gender person|Dead at %s" -msgstr "" +msgstr "Cinsiyeti bilinmeyen kişi|%s tarihinde öldü" #: PhpGedView/phpgedview.glade:12 msgid "- default -" -msgstr "" +msgstr "- varsayılan -" #: PhpGedView/phpgedview.glade:19 PhpGedView/phpgedview.glade:85 msgid "phpGedView import" -msgstr "" +msgstr "phpGedView içe aktarma" #: PhpGedView/phpgedview.glade:103 msgid "http://" -msgstr "" +msgstr "http://" #: PhpGedView/phpgedview.glade:170 msgid "Username:" -msgstr "" +msgstr "Kullanıcı adı:" #: PhpGedView/phpgedview.glade:182 msgid "Password:" -msgstr "" +msgstr "Şifre:" #: PhpGedView/phpgedview.gpr.py:24 msgid "PhpGedView" -msgstr "" +msgstr "PhpGedView" #: PhpGedView/phpgedview.gpr.py:25 msgid "Download a GEDCOM file from a phpGedView server." -msgstr "" +msgstr "Bir phpGedView sunucusundan bir GEDCOM dosyası indir." #: PhpGedView/phpgedviewconnector.py:204 PhpGedView/phpgedviewconnector.py:215 msgid "Fetching index list..." -msgstr "" +msgstr "Dizin listesi getiriliyor..." #: PhpGedView/phpgedviewconnector.py:218 PhpGedView/phpgedviewconnector.py:222 msgid "Fetching records..." -msgstr "" +msgstr "Kayıtlar getiriliyor..." #: PhpGedView/phpgedviewconnector.py:327 msgid "Logging in..." -msgstr "" +msgstr "Giriş yapılıyor..." #: PhpGedView/phpgedviewconnector.py:330 msgid "Fetching GEDCOM..." -msgstr "" +msgstr "GEDCOM getiriliyor..." #: PhpGedView/phpgedviewconnector.py:334 msgid "Importing GEDCOM..." -msgstr "" +msgstr "GEDCOM içe aktarılıyor..." #: PhpGedView/phpgedviewconnector.py:341 msgid "Error: login failed" -msgstr "" +msgstr "Hata: Giriş başarısız" #: PhpGedView/phpgedviewconnector.py:342 PhpGedView/phpgedviewconnector.py:367 msgid "done." -msgstr "" +msgstr "Tamamlandı." #: PhpGedView/phpgedviewconnector.py:347 msgid "Connecting..." -msgstr "" +msgstr "Bağlanılıyor..." #: PhpGedView/phpgedviewconnector.py:349 msgid "Get version..." -msgstr "" +msgstr "Sürüm alınıyor..." #: PhpGedView/phpgedviewconnector.py:352 #, python-format msgid "Version %s" -msgstr "" +msgstr "Sürüm %s" #: PhpGedView/phpgedviewconnector.py:353 msgid "Reading file list..." -msgstr "" +msgstr "Dosya listesi okunuyor..." #: PhpGedView/phpgedviewconnector.py:366 msgid "Error: Invalid URL" -msgstr "" +msgstr "Hata: Geçersiz URL" #: PlaceCleanup/placecleanup.glade:27 msgid "City, County, State, Country" -msgstr "" +msgstr "Şehir, İlçe, Eyalet, Ülke" #: PlaceCleanup/placecleanup.glade:89 msgid "The current Primary name." -msgstr "" +msgstr "Geçerli birincil ad." #: PlaceCleanup/placecleanup.glade:102 msgid "The postal code for the place, if any." -msgstr "" +msgstr "Varsa, yerin posta kodu." #: PlaceCleanup/placecleanup.glade:114 msgid "What type of place this is. Eg 'Country', 'City', ... ." -msgstr "" +msgstr "Bu yerin türü. Örneğin 'Ülke', 'Şehir', ..." #: PlaceCleanup/placecleanup.glade:133 msgid "Check this box if you want to keep the original Postal code." -msgstr "" +msgstr "Orijinal Posta kodunu saklamak istiyorsanız bu kutuyu işaretleyin." #: PlaceCleanup/placecleanup.glade:148 msgid "Check this box if you want to keep the original Place Type." -msgstr "" +msgstr "Orijinal Yer Türünü saklamak istiyorsanız bu kutuyu işaretleyin." #: PlaceCleanup/placecleanup.glade:166 PlaceCleanup/placecleanup.glade:180 #: PlaceCleanup/placecleanup.glade:339 PlaceCleanup/placecleanup.glade:353 msgid "Orig" -msgstr "" +msgstr "Orijinal" #: PlaceCleanup/placecleanup.glade:280 msgid "Check this box if you want to keep the original Latitude and Longitude." -msgstr "" +msgstr "Orijinal Enlem ve Boylamı saklamak istiyorsanız bu kutuyu işaretleyin." #: PlaceCleanup/placecleanup.glade:295 msgid "The Gramps ID.." -msgstr "" +msgstr "Gramps Kimliği.." #: PlaceCleanup/placecleanup.glade:322 msgid "Check this box if you want to keep the original Gramps ID." -msgstr "" +msgstr "Orijinal Gramps Kimliğini saklamak istiyorsanız bu kutuyu işaretleyin." #: PlaceCleanup/placecleanup.glade:391 msgid "" @@ -19856,68 +20113,75 @@ msgid "" "Select one or more rows and press 'Keep' button to toggle the 'include into " "place' status ." msgstr "" +"Alternatif Adlar\n" +"Bir veya daha fazla satır seçin ve 'Yere dahil et' durumunu değiştirmek için " +"'Sakla' düğmesine basın." #: PlaceCleanup/placecleanup.glade:406 msgid "Inc" -msgstr "" +msgstr "Dahil Et" #: PlaceCleanup/placecleanup.glade:446 msgid "Lang" -msgstr "" +msgstr "Dil" #: PlaceCleanup/placecleanup.glade:520 msgid "" "Select one or more rows and use this button to keep the alternative names." msgstr "" +"Bir veya daha fazla satır seçin ve alternatif adları saklamak için bu " +"düğmeyi kullanın." #: PlaceCleanup/placecleanup.glade:536 msgid "Select one row and use this button to make the row the primary name." -msgstr "" +msgstr "Bir satır seçin ve satırı birincil ad yapmak için bu düğmeyi kullanın." #: PlaceCleanup/placecleanup.glade:548 msgid "Discard" -msgstr "" +msgstr "At" #: PlaceCleanup/placecleanup.glade:552 msgid "" "Select one or more rows and use this button to discard the alternative names." msgstr "" +"Bir veya daha fazla satır seçin ve alternatif adları atmak için bu düğmeyi " +"kullanın." #: PlaceCleanup/placecleanup.glade:580 msgid "Cancel the operation, no changes are made to your Place." -msgstr "" +msgstr "İşlemi iptal edin, Yerinizde hiçbir değişiklik yapılmaz." #: PlaceCleanup/placecleanup.glade:595 msgid "Store the values in your Place." -msgstr "" +msgstr "Değerleri Yerinizde saklayın." #: PlaceCleanup/placecleanup.glade:677 msgid "Keep Web Links" -msgstr "" +msgstr "Web Bağlantılarını Sakla" #: PlaceCleanup/placecleanup.glade:681 msgid "Puts web links (typically wikipedia) into your place." -msgstr "" +msgstr "Yerinizin içine web bağlantıları (genellikle Wikipedia) ekler." #: PlaceCleanup/placecleanup.glade:693 msgid "Add citation and source to Place" -msgstr "" +msgstr "Yere alıntı ve kaynak ekle" #: PlaceCleanup/placecleanup.glade:697 msgid "This Adds a Citation to your place, citing GeoNames." -msgstr "" +msgstr "Bu, GeoNames'i kaynak gösteren bir alıntı ekler." #: PlaceCleanup/placecleanup.glade:711 msgid "GeoNames User ID" -msgstr "" +msgstr "GeoNames Kullanıcı Kimliği" #: PlaceCleanup/placecleanup.glade:723 msgid "Enter you Geonames user ID here." -msgstr "" +msgstr "GeoNames kullanıcı kimliğinizi buraya girin." #: PlaceCleanup/placecleanup.glade:735 msgid "Alternative Names Languages to keep" -msgstr "" +msgstr "Saklanacak Alternatif Ad Dilleri" #: PlaceCleanup/placecleanup.glade:747 msgid "" @@ -19928,10 +20192,16 @@ msgid "" "If you often see an un-checked name in a specific language you want, add the " "language code here." msgstr "" +"Saklamak istediğiniz dillerin iki veya üç harfli kodlarını boşluklarla " +"ayırarak girin.\n" +"Örneğin \"en fr ru\".\n" +"Bu diller, Adlar sonuç ekranında başlangıçta işaretlenecektir.\n" +"Belirli bir dilde sık sık işaretlenmemiş bir ad görüyorsanız, dil kodunu " +"buraya ekleyin." #: PlaceCleanup/placecleanup.glade:763 msgid "Currently Enclosed Places" -msgstr "" +msgstr "Şu Anda Kapsamlı Yerler" #: PlaceCleanup/placecleanup.glade:775 msgid "" @@ -19942,14 +20212,21 @@ msgid "" "is changed to the next level GeoNames place. This may result in some unused " "places remaining in your database." msgstr "" +"Zaten başka bir yerin içinde bulunan bir yeri düzenlerken bunun nasıl " +"işleneceğini belirler.\n" +"'Geçerli Kapsamı Sakla' seçilirse, kapsayan yerde hiçbir değişiklik " +"yapılmaz.\n" +"'GeoNames Kapsamıyla Değiştir' seçilirse, kapsanan yer bir sonraki " +"GeoNames hiyerarşi düzeyindeki yerle değiştirilir. Bu durum veritabanınızda " +"kullanılmayan bazı yerlerin kalmasına neden olabilir." #: PlaceCleanup/placecleanup.glade:780 msgid "Keep current Enclosure" -msgstr "" +msgstr "Geçerli Kapsamı Sakla" #: PlaceCleanup/placecleanup.glade:795 msgid "Replace with GeoNames Enclosure" -msgstr "" +msgstr "GeoNames Kapsamıyla Değiştir" #: PlaceCleanup/placecleanup.glade:851 msgid "" @@ -19957,15 +20234,20 @@ msgid "" "You can edit this to help find other places, particularly to reduce the " "number of results." msgstr "" +"Geçerli Yer başlığı.\n" +"Özellikle sonuç sayısını azaltmak amacıyla diğer yerleri bulmayı " +"kolaylaştırmak için bunu düzenleyebilirsiniz." #: PlaceCleanup/placecleanup.glade:899 msgid "Search your local places and GeoNames for a match." -msgstr "" +msgstr "Eşleşme bulmak için yerel yerlerinizi ve GeoNames'i arayın." #: PlaceCleanup/placecleanup.glade:915 msgid "" "Continue to merge (for local places), or to complete a place (for GeoNames)." msgstr "" +"(Yerel yerler için) birleştirmeye veya (GeoNames için) yeri tamamlamaya " +"devam edin." #: PlaceCleanup/placecleanup.glade:932 msgid "" @@ -19973,14 +20255,17 @@ msgid "" "For use when the initial component of the Title is too detailed to be found " "in the gazetteer." msgstr "" +"Başlığı böl, elle düzenle ve hiyerarşinin bir sonraki düzeyiyle " +"ilişkilendir. Başlığın ilk bileşeni dizinde bulunamayacak kadar ayrıntılı " +"olduğunda kullanılır." #: PlaceCleanup/placecleanup.glade:958 msgid "Next Place" -msgstr "" +msgstr "Sonraki Yer" #: PlaceCleanup/placecleanup.glade:962 msgid "Find an incomplete place." -msgstr "" +msgstr "Eksik bir yeri bulun." #: PlaceCleanup/placecleanup.glade:985 msgid "Set preferences" @@ -19988,33 +20273,40 @@ msgstr "Tercihleri ayarla" #: PlaceCleanup/placecleanup.glade:1008 msgid "Select one of these places to continue the cleanup process." -msgstr "" +msgstr "Temizleme işlemine devam etmek için bu yerlerden birini seçin." #: PlaceCleanup/placecleanup.glade:1034 msgid "Search result places" -msgstr "" +msgstr "Arama sonucu yerleri" #: PlaceCleanup/placecleanup.glade:1085 PlaceCleanup/placecleanup.py:266 msgid "" "No\n" "Matches" msgstr "" +"Hayır\n" +"Eşleşmeler" #: PlaceCleanup/placecleanup.glade:1099 msgid "" "Checking the 'Populated' checkbox causes the GeoNames search to include " "'Populted places', places where people live and work." msgstr "" +"'Yerleşimli' onay kutusunu işaretlemek, GeoNames aramasının 'Yerleşimli " +"yerler'i, yani insanların yaşadığı ve çalıştığı yerleri içermesine neden " +"olur." #: PlaceCleanup/placecleanup.glade:1116 msgid "Populated" -msgstr "" +msgstr "Yerleşimli" #: PlaceCleanup/placecleanup.glade:1128 msgid "" "Checking the 'Admin' checkbox cause the GeoNames search to include " "adminstrative subdivisions in the search (countries, states, counties etc.)." msgstr "" +"'Yönetici' onay kutusunu işaretlemek, GeoNames aramasına idari alt bölümleri " +"(ülkeler, eyaletler, ilçeler vb.) dahil eder." #: PlaceCleanup/placecleanup.glade:1145 msgid "" @@ -20022,14 +20314,16 @@ msgid "" "variety of other places in the search (churches, burial grounds, buildings " "etc.)." msgstr "" +"'Yer' onay kutusunu işaretlemek, GeoNames aramasına çok çeşitli diğer " +"yerleri (kiliseler, mezarlıklar, binalar vb.) dahil eder." #: PlaceCleanup/placecleanup.glade:1160 msgid "Admin" -msgstr "" +msgstr "Yönetici" #: PlaceCleanup/placecleanup.glade:1171 msgid "Spot" -msgstr "" +msgstr "Yer" #: PlaceCleanup/placecleanup.gpr.py:28 PlaceCleanup/placecleanup.gpr.py:46 msgid "Place Cleanup" @@ -20040,6 +20334,8 @@ msgid "" "Place Cleanup Gramplet assists in merging places, as well as completing " "places from the GeoNames web database" msgstr "" +"Yer Temizleme Gramplet'i, yerlerin birleştirilmesine ve GeoNames web " +"veritabanından yer bilgilerinin tamamlanmasına yardımcı olur" #: PlaceCleanup/placecleanup.py:293 #, python-format @@ -20048,24 +20344,29 @@ msgid "" "Local\n" "Matches" msgstr "" +"%s\n" +"Yerel\n" +"Eşleşmeler" #: PlaceCleanup/placecleanup.py:295 msgid "Find GeoNames" -msgstr "" +msgstr "GeoNames Bul" #: PlaceCleanup/placecleanup.py:303 msgid "Need to set GeoNames ID" -msgstr "" +msgstr "GeoNames Kimliği ayarlanmalı" #: PlaceCleanup/placecleanup.py:304 msgid "Use the Help button for more information" -msgstr "" +msgstr "Daha fazla bilgi için Yardım düğmesini kullanın" #: PlaceCleanup/placecleanup.py:337 msgid "" "Try changing the Title, or use the \"Edit\" button to finish this level of " "the place manually." msgstr "" +"Başlığı değiştirmeyi deneyin veya bu yer seviyesini el ile tamamlamak için " +"\"Düzenle\" düğmesini kullanın." #: PlaceCleanup/placecleanup.py:347 #, python-format @@ -20074,11 +20375,14 @@ msgid "" "GeoNames\n" "Matches" msgstr "" +"%s\n" +"GeoNames\n" +"Eşleşmeler" #: PlaceCleanup/placecleanup.py:353 #, python-format msgid "%s matches were found" -msgstr "" +msgstr "%s eşleşme bulundu" #: PlaceCleanup/placecleanup.py:354 msgid "" @@ -20086,42 +20390,51 @@ msgid "" "To see additional results, press the search button again.\n" "Or try changing the Title with more detail, such as a country." msgstr "" +"Yalnızca 10 eşleşme gösteriliyor.\n" +"Ek sonuçları görmek için arama düğmesine tekrar basın.\n" +"Veya Başlığı daha ayrıntılı bilgilerle, örneğin bir ülke ile değiştirmeyi " +"deneyin." #: PlaceCleanup/placecleanup.py:421 PlaceCleanup/placecleanup.py:426 msgid "Problem getting data from web" -msgstr "" +msgstr "Web'den veri alma sorunu" #: PlaceCleanup/placecleanup.py:427 msgid "Web request Timeout, you can try again..." -msgstr "" +msgstr "Web isteği zaman aşımına uğradı, tekrar deneyebilirsiniz..." #: PlaceCleanup/placecleanup.py:435 msgid "Problem getting data from GeoNames" -msgstr "" +msgstr "GeoNames'ten veri alma sorunu" #: PlaceCleanup/placecleanup.py:481 msgid "" "One of the places you are merging encloses the other!\n" "Please choose another place." msgstr "" +"Birleştirdiğiniz yerlerden biri diğerini kapsıyor!\n" +"Lütfen başka bir yer seçin." #: PlaceCleanup/placecleanup.py:759 msgid "" "The place you chose is enclosed in the place you are workin on!\n" "Please cancel and choose another place." msgstr "" +"Seçtiğiniz yer, üzerinde çalıştığınız yerin içinde yer alıyor!\n" +"Lütfen iptal edin ve başka bir yer seçin." #: PlaceCleanup/placecleanup.py:946 msgid "GeoNames web site" -msgstr "" +msgstr "GeoNames web sitesi" #: PlaceCleanup/placecleanup.py:951 msgid "GeoNames author" -msgstr "" +msgstr "GeoNames yazarı" #: PlaceCleanup/placecleanup.py:956 msgid "GeoNames was founded by Marc Wick. You can reach him at " msgstr "" +"GeoNames, Marc Wick tarafından kurulmuştur. Ona şu adresten ulaşabilirsiniz " #: PlaceCleanup/placecleanup.py:959 msgid "" @@ -20129,54 +20442,63 @@ msgid "" "Switzerland.\n" "This work is licensed under a " msgstr "" +"GeoNames, Unxos GmbH, Weingartenstrasse 8, 8708 Männedorf, İsviçre'nin bir " +"projesidir.\n" +"Bu çalışma şu lisans altında lisanslanmıştır " #: PlaceCleanup/placecleanup.py:962 msgid "Creative Commons Attribution 3.0 License" -msgstr "" +msgstr "Creative Commons Attribution 3.0 Lisansı" #: PlaceCleanup/placecleanup.py:974 #, python-format msgid "Add Souce/Repo/Note (%s)" -msgstr "" +msgstr "Kaynak/Depo/Not Ekle (%s)" #: PlaceCleanup/placecleanup.py:1075 msgid "This Place is not used!" -msgstr "" +msgstr "Bu Yer kullanılmıyor!" #: PlaceCleanup/placecleanup.py:1076 msgid "" "You should delete it, or, if it contains useful notes or other data, use the " "Find to merge it into a valid place." msgstr "" +"Silmeniz veya yararlı notlar veya diğer veriler içeriyorsa, geçerli bir yere " +"birleştirmek için Bul özelliğini kullanmanız gerekir." #: PlaceCompletion/PlaceCompletion.gpr.py:26 msgid "PlaceCompletion" -msgstr "" +msgstr "Yer Tamamlama" #: PlaceCompletion/PlaceCompletion.gpr.py:28 msgid "" "Provides a browsable list of selected places, with possibility to complete/" "parse/set the attribute fields." msgstr "" +"Seçilen yerlerin göz atılabilir bir listesini sağlar ve öznitelik alanlarını " +"tamamlama/ayrıştır/ayarlama olanağı sunar." #: PlaceCompletion/PlaceCompletion.py:95 msgid "" "Place Completion by parsing, file lookup and batch setting of place " "attributes" msgstr "" +"Yer Tamamlama, ayrıştırma, dosya arama ve yer özniteliklerinin toplu " +"ayarlanması yoluyla gerçekleştirilir" #: PlaceCompletion/PlaceCompletion.py:258 msgid "Error in PlaceCompletion.py" -msgstr "" +msgstr "PlaceCompletion.py dosyasında hata" #: PlaceCompletion/PlaceCompletion.py:259 msgid "Non existing group used in get" -msgstr "" +msgstr "get işleminde mevcut olmayan grup kullanıldı" #: PlaceCompletion/PlaceCompletion.py:282 #, python-format msgid "PlaceCompletion is unable to create %s %s" -msgstr "" +msgstr "Yer Tamamlama %s %s oluşturamıyor" #: PlaceCompletion/PlaceCompletion.py:340 msgid "Places tool" @@ -20184,41 +20506,42 @@ msgstr "Yerler aracı" #: PlaceCompletion/PlaceCompletion.py:408 msgid "Missing regex groups in match lat/lon" -msgstr "" +msgstr "Enlem/boylam eşleşmesinde eksik regex grupları" #: PlaceCompletion/PlaceCompletion.py:409 #, python-format msgid "" "Regex groups %(lat)s and %(lon)s must be present in lat/lon match. Quiting" msgstr "" +"Enlem/boylam eşleşmesinde %(lat)s ve %(lon)s regex grupları bulunmalıdır" #: PlaceCompletion/PlaceCompletion.py:419 msgid "Non valid regex for match lat/lon" -msgstr "" +msgstr "Geçersiz enlem/boylam eşleştirme düzenli ifadesi" #: PlaceCompletion/PlaceCompletion.py:420 msgid "Non valid regular expression given to find lat/lon. Quiting." -msgstr "" +msgstr "Enlem/boylam bulmak için geçersiz bir düzenli ifade verildi. Çıkılıyor." #: PlaceCompletion/PlaceCompletion.py:479 msgid "Finding Places and appropriate changes" -msgstr "" +msgstr "Yerleri bulma ve uygun değişiklikler" #: PlaceCompletion/PlaceCompletion.py:481 msgid "Filtering" -msgstr "" +msgstr "Filtreleme" #: PlaceCompletion/PlaceCompletion.py:491 msgid "Loading lat/lon file in Memory..." -msgstr "" +msgstr "Enlem/boylam dosyası belleğe yükleniyor..." #: PlaceCompletion/PlaceCompletion.py:497 msgid "Examining places" -msgstr "" +msgstr "Yerler inceleniyor" #: PlaceCompletion/PlaceCompletion.py:685 msgid "Set Tag" -msgstr "" +msgstr "Etiket Ayarla" #: PlaceCompletion/PlaceCompletion.py:687 msgid "Doing Place changes" @@ -20226,16 +20549,16 @@ msgstr "Yer değişiklikleri yapma" #: PlaceCompletion/PlaceCompletion.py:731 msgid "No place record was modified." -msgstr "" +msgstr "Hiçbir yer kaydı değiştirilmedi." #: PlaceCompletion/PlaceCompletion.py:733 msgid "1 place record was modified." -msgstr "" +msgstr "1 yer kaydı değiştirildi." #: PlaceCompletion/PlaceCompletion.py:735 #, python-format msgid "%d place records were modified." -msgstr "" +msgstr "%d yer kaydı değiştirildi." #: PlaceCompletion/PlaceCompletion.py:736 msgid "Change places" @@ -20243,41 +20566,41 @@ msgstr "Yerleri değiştir" #: PlaceCompletion/PlaceCompletion.py:809 msgid "The selected file is a directory, not a file." -msgstr "" +msgstr "Seçilen öğe bir dosya değil, bir dizindir." #: PlaceCompletion/PlaceCompletion.py:820 msgid "The file you want to access is not a regular file." -msgstr "" +msgstr "Erişmek istediğiniz öğe normal bir dosya değil." #: PlaceCompletion/PlaceCompletion.py:826 msgid "The file does not exist." -msgstr "" +msgstr "Dosya mevcut değil." #: PlaceCompletion/PlaceCompletion.py:858 msgid "Problem reading file" -msgstr "" +msgstr "Dosya okuma sorunu" #: PlaceCompletion/PlaceCompletion.py:970 #: PlaceCompletion/PlaceCompletion.py:982 #, python-format msgid "invalid lat or lon value, %(lat)s, %(lon)s" -msgstr "" +msgstr "Geçersiz enlem veya boylam değeri, %(lat)s, %(lon)s" #: PlaceCompletion/PlaceCompletion.py:1135 msgid "No lat/lon conversion" -msgstr "" +msgstr "Enlem/boylam dönüştürmesi yok" #: PlaceCompletion/PlaceCompletion.py:1136 msgid "All in degree notation" -msgstr "" +msgstr "Tüm değerler derece cinsinden" #: PlaceCompletion/PlaceCompletion.py:1137 msgid "All in decimal notation" -msgstr "" +msgstr "Tüm değerler ondalık sayı cinsinden" #: PlaceCompletion/PlaceCompletion.py:1138 msgid "Correct -50° in 50°S" -msgstr "" +msgstr "-50° değerini 50°G olarak düzelt" #: PlaceCompletion/PlaceCompletion.py:1142 msgid "No changes" @@ -20285,91 +20608,91 @@ msgstr "Değişiklik yok" #: PlaceCompletion/PlaceCompletion.py:1143 msgid "City[, State]" -msgstr "" +msgstr "Şehir[, Eyalet]" #: PlaceCompletion/PlaceCompletion.py:1145 msgid "City,PostalCode,Country" -msgstr "" +msgstr "Şehir,Posta Kodu,Ülke" #: PlaceCompletion/PlaceCompletion.py:1147 msgid "City[(Street;Locality;Parish)],PostalCode,Country" -msgstr "" +msgstr "Şehir[(Sokak;Yerleşim Yeri;İlçe)],Posta Kodu,Ülke" #: PlaceCompletion/PlaceCompletion.py:1149 msgid "TitleStart [, City] [, State]" -msgstr "" +msgstr "BaşlıkBaşlangıcı [, Şehir] [, Eyalet]" #: PlaceCompletion/PlaceCompletion.py:1151 msgid "TitleStart [, City] [, County] [, State] [, Country]" -msgstr "" +msgstr "BaşlıkBaşlangıcı [, Şehir] [, İlçe] [, Eyalet] [, Ülke]" #: PlaceCompletion/PlaceCompletion.py:1153 msgid "TitleStart [, City] [, Zip] [, County] [, State] [, Country]" -msgstr "" +msgstr "BaşlıkBaşlangıcı [, Şehir] [, Posta Kodu] [, İlçe] [, Eyalet] [, Ülke]" #: PlaceCompletion/PlaceCompletion.py:1177 msgid "Don't search" -msgstr "" +msgstr "Arama yapmayın" #: PlaceCompletion/PlaceCompletion.py:1180 msgid "GeoNames country file, city search" -msgstr "" +msgstr "GeoNames ülke dosyası, şehir araması" #: PlaceCompletion/PlaceCompletion.py:1185 msgid "GeoNames country file, city localized variants search" -msgstr "" +msgstr "GeoNames ülke dosyası, yerelleştirilmiş şehir varyantları araması" #: PlaceCompletion/PlaceCompletion.py:1190 msgid "GeoNames country file, county/city search" -msgstr "" +msgstr "GeoNames ülke dosyası, ilçe/şehir araması" #: PlaceCompletion/PlaceCompletion.py:1202 msgid "GeoNames country file, title begin, general search" -msgstr "" +msgstr "GeoNames ülke dosyası, başlık başlangıcı, genel arama" #: PlaceCompletion/PlaceCompletion.py:1207 msgid "GeoNames USA state file, city search" -msgstr "" +msgstr "GeoNames ABD eyalet dosyası, şehir araması" #: PlaceCompletion/PlaceCompletion.py:1221 msgid "GNS Geonet country file, city search" -msgstr "" +msgstr "GNS Geonet ülke dosyası, şehir araması" #: PlaceCompletion/PlaceCompletion.py:1228 msgid "GNS Geonet country file, county/city search" -msgstr "" +msgstr "GNS Geonet ülke dosyası, ilçe/şehir araması" #: PlaceCompletion/PlaceCompletion.py:1236 msgid "GNS Geonet country file, title begin, general search" -msgstr "" +msgstr "GNS Geonet ülke dosyası, başlık başlangıcı, genel arama" #: PlaceCompletion/PlaceCompletion.py:1244 msgid "Wikipedia CSV Dump" -msgstr "" +msgstr "Wikipedia CSV Dökümü" #: PlaceCompletion/PlaceCompletion.py:1332 msgid "All Places" -msgstr "" +msgstr "Tüm Yerler" #: PlaceCompletion/PlaceCompletion.py:1335 msgid "No Latitude/Longitude given" -msgstr "" +msgstr "Enlem/Boylam verilmedi" #: PlaceCompletion/placecompletion.glade:85 msgid "_Google Maps" -msgstr "" +msgstr "_Google Haritalar" #: PlaceCompletion/placecompletion.glade:112 msgid "Apply all suggested changes" -msgstr "" +msgstr "Önerilen tüm değişiklikleri uygula" #: PlaceCompletion/placecompletion.glade:148 msgid "Selection of the Places you want to complete:" -msgstr "" +msgstr "Tamamlamak istediğiniz Yerlerin seçimi:" #: PlaceCompletion/placecompletion.glade:195 msgid "Parish:" -msgstr "" +msgstr "Bölge:" #: PlaceCompletion/placecompletion.glade:271 msgid "Place _filter:" @@ -20377,63 +20700,67 @@ msgstr "Yer _süzgeci:" #: PlaceCompletion/placecompletion.glade:336 msgid "Count_ry:" -msgstr "" +msgstr "Ül_ke:" #: PlaceCompletion/placecompletion.glade:366 msgid "C_enter latitude:" -msgstr "" +msgstr "M_erkez enlemi:" #: PlaceCompletion/placecompletion.glade:383 msgid "Center longitude:" -msgstr "" +msgstr "Merkez boylamı:" #: PlaceCompletion/placecompletion.glade:430 msgid "Places in a rectangle:" -msgstr "" +msgstr "Bir dikdörtgen içindeki yerler:" #: PlaceCompletion/placecompletion.glade:460 msgid "Height:" -msgstr "" +msgstr "Yükseklik:" #: PlaceCompletion/placecompletion.glade:527 msgid "1. Look up latitude and longitude:" -msgstr "" +msgstr "1. Enlem ve boylamı arayın:" #: PlaceCompletion/placecompletion.glade:554 msgid "Search in:" -msgstr "" +msgstr "Şurada ara:" #: PlaceCompletion/placecompletion.glade:566 msgid "Select A File" -msgstr "" +msgstr "Bir Dosya Seç" #: PlaceCompletion/placecompletion.glade:579 msgid "Parse as:" -msgstr "" +msgstr "Şu şekilde ayrıştır:" #: PlaceCompletion/placecompletion.glade:619 msgid "2. Conversion of existing title or position:" -msgstr "" +msgstr "2. Mevcut başlığın veya konumun dönüştürülmesi:" #: PlaceCompletion/placecompletion.glade:649 msgid "Change title into:" -msgstr "" +msgstr "Başlığı şuna değiştir:" #: PlaceCompletion/placecompletion.glade:664 msgid "Convert lat/lon as:" -msgstr "" +msgstr "Enlem/boylamı şuna dönüştür:" #: PlaceCompletion/placecompletion.glade:701 msgid "" "Or define a simple\n" "title format:" msgstr "" +"Veya basit bir\n" +"başlık biçimi tanımla:" #: PlaceCompletion/placecompletion.glade:730 msgid "" "Available variables: city, street, locality, parish, county, state, country, " "postal_code" msgstr "" +"Kullanılabilir değişkenler: şehir, cadde, yerleşim yeri, mahalle, ilçe, " +"eyalet, ülke, posta kodu" #: PlaceCompletion/placecompletion.glade:820 msgid "" @@ -20442,30 +20769,35 @@ msgid "" "Press Tab on a row or Google Maps button to see place on a map. Press Apply " "to do all changes automatically" msgstr "" +"Sil düğmesine basarak bir satırı silin, önceden girilmiş değişikliklerle " +"yeri düzenlemek için satıra çift tıklayın, \n" +"bir satırda Tab tuşuna veya Google Haritalar düğmesine basarak yeri haritada " +"görüntüleyin. Tüm değişiklikleri otomatik olarak uygulamak için Uygula " +"düğmesine basın" #: PlaceCoordinateGramplet/PlaceCoordinateGeoView.py:264 msgid "Place Coordinate Gramplet" -msgstr "" +msgstr "Yer Koordinatları Grampleti" #: PlaceCoordinateGramplet/PlaceCoordinateGeoView.py:764 msgid "Map Menu" -msgstr "" +msgstr "Harita Menüsü" #: PlaceCoordinateGramplet/PlaceCoordinateGeoView.py:775 msgid "Add city as place" -msgstr "" +msgstr "Şehri yer olarak ekle" #: PlaceCoordinateGramplet/PlaceCoordinateGeoView.py:788 msgid "Add addess as place" -msgstr "" +msgstr "Adresi yer olarak ekle" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:24 msgid "Place Coordinate Gramplet view" -msgstr "" +msgstr "Yer Koordinatları Gramplet görünümü" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:25 msgid "View for the place coordinate gramplet." -msgstr "" +msgstr "Yer koordinatları gramplet görünümü." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:43 msgid "Place and Coordinates" @@ -20473,7 +20805,7 @@ msgstr "Yer ve Koordinatlar" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:44 msgid "Gramplet that simplifies setting the coordinates of a place" -msgstr "" +msgstr "Bir yerin koordinatlarını ayarlamayı basitleştiren gramplet" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:53 msgid "Place Coordinates" @@ -20481,86 +20813,88 @@ msgstr "Yer Koordinatları" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:215 msgid "The place was not found. You may clarify the search keywords." -msgstr "" +msgstr "Yer bulunamadı. Arama anahtar kelimelerini netleştirebilirsiniz." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:219 #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:247 msgid "Failed to search for the coordinates due to some unexpected error." -msgstr "" +msgstr "Beklenmedik bir hata nedeniyle koordinat araması başarısız oldu." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:244 msgid "The place was not found." -msgstr "" +msgstr "Yer bulunamadı." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:269 #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:300 msgid "The place was not identified." -msgstr "" +msgstr "Yer tanımlanamadı." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:271 #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:302 msgid "Coordinates were not given." -msgstr "" +msgstr "Koordinatlar verilmedi." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:286 msgid "Failed to interpret the input format." -msgstr "" +msgstr "Giriş biçimi yorumlanamadı." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:332 #, python-brace-format msgid "DB entry [{id}] {name}:" -msgstr "" +msgstr "Veritabanı girişi [{id}] {name}:" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:337 #: PlaceCoordinateGramplet/placecoordinate.glade:132 msgid "Nothing has been searched yet" -msgstr "" +msgstr "Henüz hiçbir şey aranmadı" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.py:359 msgid "No place is active" -msgstr "" +msgstr "Etkin yer yok" #: PlaceCoordinateGramplet/placecoordinate.glade:15 msgid "Search for:" -msgstr "" +msgstr "Aranacak yer:" #: PlaceCoordinateGramplet/placecoordinate.glade:26 msgid "Found place:" -msgstr "" +msgstr "Bulunan yer:" #: PlaceCoordinateGramplet/placecoordinate.glade:53 msgid "No DB entry is selected" -msgstr "" +msgstr "Seçili veritabanı girişi yok" #: PlaceCoordinateGramplet/placecoordinate.glade:68 msgid "Postal-Code:" -msgstr "" +msgstr "Posta Kodu:" #: PlaceCoordinateGramplet/placecoordinate.glade:116 msgid "Show found place externally in Google Maps" -msgstr "" +msgstr "Bulunan yeri Google Haritalarda harici olarak göster" #: PlaceCoordinateGramplet/placecoordinate.glade:203 msgid "" "Take last clicked position\n" "from Geography map" msgstr "" +"Coğrafya haritasından\n" +"en son tıklanan konumu al" #: PlaceCoordinateGramplet/placecoordinate.glade:218 msgid "Search location from DB" -msgstr "" +msgstr "Veritabanından konum ara" #: PlaceCoordinateGramplet/placecoordinate.glade:232 msgid "Apply geo location to Database" -msgstr "" +msgstr "Coğrafi konumu veritabanına uygula" #: PlaceCoordinateGramplet/placecoordinate.glade:269 msgid "Go" -msgstr "" +msgstr "Git" #: PlaceCoordinateGramplet/placecoordinate.glade:290 msgid "Show help" -msgstr "" +msgstr "Yardımı göster" #: PlaceUpdate/PlaceUpdate.gpr.py:4 PlaceUpdate/PlaceUpdate.gpr.py:15 msgid "PlaceUpdate" @@ -20568,48 +20902,50 @@ msgstr "YerGüncelle" #: PlaceUpdate/PlaceUpdate.gpr.py:5 msgid "Gramplet to manipulate multiple places" -msgstr "" +msgstr "Birden fazla yeri yönetmek için Gramplet" #: PlaceUpdate/PlaceUpdate.py:56 msgid "Set properties for multiple places" -msgstr "" +msgstr "Birden fazla yer için özellikler ayarla" #: PlaceUpdate/PlaceUpdate.py:96 msgid "" "This gramplet allows setting properties for multiple places at the same time" msgstr "" +"Bu Gramplet, aynı anda birden fazla yer için özellikler ayarlamanıza olanak " +"tanır" #: PlaceUpdate/PlaceUpdate.py:111 msgid "New enclosing place" -msgstr "" +msgstr "Yeni kapsayıcı yer" #: PlaceUpdate/PlaceUpdate.py:119 msgid "Timespan:" -msgstr "" +msgstr "Zaman aralığı:" #: PlaceUpdate/PlaceUpdate.py:123 msgid "Set timespan for enclosing places" -msgstr "" +msgstr "Kapsayıcı yerler için zaman aralığını ayarla" #: PlaceUpdate/PlaceUpdate.py:159 msgid "Clear original enclosing places" -msgstr "" +msgstr "Orijinal kapsayıcı yerleri temizle" #: PlaceUpdate/PlaceUpdate.py:162 msgid "Clear tags" -msgstr "" +msgstr "Etiketleri temizle" #: PlaceUpdate/PlaceUpdate.py:165 msgid "Generate hierarchy" -msgstr "" +msgstr "Hiyerarşi oluştur" #: PlaceUpdate/PlaceUpdate.py:171 msgid "use spaces as separator" -msgstr "" +msgstr "Ayırıcı olarak boşluk kullan" #: PlaceUpdate/PlaceUpdate.py:175 msgid "reverse hierarchy" -msgstr "" +msgstr "Hiyerarşiyi tersine çevir" #: PlaceUpdate/PlaceUpdate.py:181 msgid "Replace text" @@ -20617,19 +20953,19 @@ msgstr "Metni değiştir" #: PlaceUpdate/PlaceUpdate.py:184 msgid "Use regex" -msgstr "" +msgstr "Düzenli ifade kullan" #: PlaceUpdate/PlaceUpdate.py:193 msgid "Old text:" -msgstr "" +msgstr "Eski metin:" #: PlaceUpdate/PlaceUpdate.py:197 msgid "New text:" -msgstr "" +msgstr "Yeni metin:" #: PlaceUpdate/PlaceUpdate.py:209 msgid "Clear selections" -msgstr "" +msgstr "Seçimleri temizle" #: PlaceUpdate/PlaceUpdate.py:213 msgid "Apply to selected places" @@ -20637,23 +20973,23 @@ msgstr "Seçilen yerlere uygula" #: PlaceUpdate/PlaceUpdate.py:256 msgid "Setting place properties" -msgstr "" +msgstr "Seçilen yerlere uygula" #: PlaceUpdate/PlaceUpdate.py:294 msgid "Regex operation failed: {}" -msgstr "" +msgstr "Düzenli ifade işlemi başarısız oldu: {}" #: PluginManager/PluginManager.gpr.py:30 msgid "Plugin Manager Enhanced" -msgstr "" +msgstr "Gelişmiş Eklenti Yöneticisi" #: PluginManager/PluginManager.gpr.py:31 msgid "An Addon/Plugin Manager with several additional capabilities" -msgstr "" +msgstr "Çeşitli ek yeteneklere sahip bir Eklenti/Yazılım Eklentisi Yöneticisi" #: PluginManager/PluginManager.py:91 msgid "Plugin Manager - Enhanced" -msgstr "" +msgstr "Eklenti Yöneticisi - Gelişmiş" #: PluginManager/PluginManager.py:163 msgid "" @@ -20662,130 +20998,140 @@ msgid "" "the addon filename to be included in the search.\n" "Word case and order is ignored." msgstr "" +"Eklentileri filtrelemek için arama kelimeleri girin.\n" +"Aramaya dahil edilecek tüm kelimelerin satırda veya\n" +"eklenti dosya adında bir yerde bulunması gerekir.\n" +"Kelime büyük/küçük harf ve sıra dikkate alınmaz." #: PluginManager/PluginManager.py:229 msgid "Restart..." -msgstr "" +msgstr "Yeniden başlat..." #: PluginManager/PluginManager.py:231 msgid "" "Please Restart Gramps so that your addon changes can be safely completed." msgstr "" +"Eklenti değişikliklerinizin güvenli bir şekilde tamamlanabilmesi için lütfen " +"Gramps'i yeniden başlatın." #: PluginManager/PluginManager.py:353 #, python-format msgid "Error removing the '%s' directory, The uninstall may have failed" msgstr "" +"'%s' dizinini kaldırmada hata oluştu, Kaldırma işlemi başarısız olmuş " +"olabilir" #: PluginManager/PluginManager.py:402 msgid "" "'*' items are supplied by 3rd party authors,\n" "strikeout items are hidden" msgstr "" +"'*' ile işaretlenen öğeler üçüncü taraf geliştiriciler tarafından sağlanır,\n" +"üstü çizili öğeler gizlidir" #: PluginManager/PluginManager.py:441 PluginManager/PluginManager.py:558 msgid "Hide" -msgstr "" +msgstr "Gizle" #: PluginManager/PluginManager.py:460 msgid "Show hidden items" -msgstr "" +msgstr "Gizli öğeleri göster" #: PluginManager/PluginManager.py:467 msgid "Show Built-in items" -msgstr "" +msgstr "Yerleşik öğeleri göster" #: PluginManager/PluginManager.py:474 msgid "* indicates 3rd party addon" -msgstr "" +msgstr "* 3. taraf eklentisini gösterir" #: PluginManager/PluginManager.py:479 msgid "Plugins" -msgstr "" +msgstr "Eklentiler" #: PluginManager/PluginManager.py:556 msgid "Unhide" -msgstr "" +msgstr "Gizliliği kaldır" #: PluginManager/PluginManager.py:575 msgid "Uninstall" -msgstr "" +msgstr "Kaldır" #: PluginManager/PluginManager.py:761 msgid "*Available" -msgstr "" +msgstr "*Kullanılabilir" #: PluginManager/PluginManager.py:781 msgid "Built-in" -msgstr "" +msgstr "Yerleşik" #: PluginManager/PluginManager.py:786 msgid "*Installed" -msgstr "" +msgstr "*Yüklü" #: PluginManager/PluginManager.py:794 msgid "Update Available" -msgstr "" +msgstr "Güncelleme mevcut" #: PostgreSQL/postgresql.gpr.py:23 msgid "PostgreSQL" -msgstr "" +msgstr "PostgreSQL" #: PostgreSQL/postgresql.gpr.py:24 msgid "_PostgreSQL Database" -msgstr "" +msgstr "_PostgreSQL Veritabanı" #: PostgreSQL/postgresql.gpr.py:25 msgid "PostgreSQL Database" -msgstr "" +msgstr "PostgreSQL Veritabanı" #: PostgreSQLEnhanced/concurrency.py:479 #, python-brace-format msgid "Object {obj_type}:{handle} was modified by another user" -msgstr "" +msgstr "{obj_type}:{handle} nesnesi başka bir kullanıcı tarafından değiştirildi" #: PostgreSQLEnhanced/migration.py:168 msgid "Starting migration..." -msgstr "" +msgstr "Geçiş başlatılıyor..." #: PostgreSQLEnhanced/migration.py:182 #, python-format msgid "Migrating %s objects..." -msgstr "" +msgstr "%s nesne taşınıyor..." #: PostgreSQLEnhanced/migration.py:193 #, python-format, python-brace-format msgid "Migrated %s {obj_type} objects" -msgstr "" +msgstr "%s adet {obj_type} nesnesi taşındı" #: PostgreSQLEnhanced/migration.py:209 msgid "Migration completed!" -msgstr "" +msgstr "Geçiş tamamlandı!" #: PostgreSQLEnhanced/migration.py:438 msgid "Starting upgrade..." -msgstr "" +msgstr "Yükseltme başlatılıyor..." #: PostgreSQLEnhanced/migration.py:470 #, python-format msgid "Upgrading %s objects..." -msgstr "" +msgstr "%s nesne yükseltiliyor..." #: PostgreSQLEnhanced/migration.py:478 msgid "Creating indexes..." -msgstr "" +msgstr "Dizinler oluşturuluyor..." #: PostgreSQLEnhanced/migration.py:489 msgid "Upgrade completed!" -msgstr "" +msgstr "Yükseltme tamamlandı!" #: PostgreSQLEnhanced/postgresqlenhanced.gpr.py:28 msgid "PostgreSQL Enhanced" -msgstr "" +msgstr "Gelişmiş PostgreSQL" #: PostgreSQLEnhanced/postgresqlenhanced.gpr.py:29 msgid "PostgreSQL _Enhanced Database" -msgstr "" +msgstr "Gelişmiş PostgreSQL Veritabanı" #: PostgreSQLEnhanced/postgresqlenhanced.gpr.py:31 msgid "" @@ -20794,12 +21140,18 @@ msgid "" "advanced users. Requires PostgreSQL 15+ with extensions. Gramps Web " "compatible." msgstr "" +"JSONB depolama, grafik veritabanı desteği (Apache AGE), vektör benzerliği " +"(pgvector) ve yapay zeka/makine öğrenimi yeteneklerine sahip gelişmiş " +"PostgreSQL arka ucu. Gelişmiş kullanıcılar için. PostgreSQL 15+ ve " +"eklentileri gerektirir. Gramps Web ile uyumludur." #: PostgreSQLEnhanced/postgresqlenhanced.py:155 msgid "" "psycopg3 is required for PostgreSQL Enhanced support. Install with: pip " "install 'psycopg[binary]'" msgstr "" +"Gelişmiş PostgreSQL desteği için psycopg3 gereklidir. Şununla kurun: pip " +"install 'psycopg[binary]'" #: PostgreSQLEnhanced/postgresqlenhanced.py:164 #, python-format @@ -20807,69 +21159,71 @@ msgid "" "psycopg3 version %(installed)s is too old. Version %(required)s or newer is " "required." msgstr "" +"psycopg3 sürümü %(installed)s çok eski. %(required)s veya daha yeni bir " +"sürüm gereklidir." #: PostgreSQLEnhanced/postgresqlenhanced.py:212 msgid "Database Backend" -msgstr "" +msgstr "Veritabanı Arka Ucu" #: PostgreSQLEnhanced/postgresqlenhanced.py:213 msgid "Database module" -msgstr "" +msgstr "Veritabanı modülü" #: PostgreSQLEnhanced/postgresqlenhanced.py:215 msgid "JSONB support" -msgstr "" +msgstr "JSONB desteği" #: PostgreSQLEnhanced/postgresqlenhanced.py:237 msgid "Version warning" -msgstr "" +msgstr "Sürüm uyarısı" #: PostgreSQLEnhanced/postgresqlenhanced.py:238 #, python-format msgid "PostgreSQL %(version)s is below recommended version %(recommended)s" -msgstr "" +msgstr "PostgreSQL %(version)s, önerilen %(recommended)s sürümünün altındadır" #: PostgreSQLEnhanced/postgresqlenhanced.py:253 msgid "Extensions" -msgstr "" +msgstr "Uzantılar" #: PostgreSQLEnhanced/postgresqlenhanced.py:274 msgid "Database size" -msgstr "" +msgstr "Veritabanı boyutu" #: PostgreSQLEnhanced/postgresqlenhanced.py:277 #, python-format msgid "%(persons)d persons, %(families)d families, %(events)d events" -msgstr "" +msgstr "%(persons)d kişi, %(families)d aile, %(events)d etkinlik" #: PostgreSQLEnhanced/postgresqlenhanced.py:1127 #: PostgreSQLEnhanced/postgresqlenhanced.py:1142 msgid "Migration manager not initialized" -msgstr "" +msgstr "Geçiş yöneticisi başlatılmadı" #: PostgreSQLEnhanced/postgresqlenhanced.py:1160 #: PostgreSQLEnhanced/postgresqlenhanced.py:1178 #: PostgreSQLEnhanced/postgresqlenhanced.py:1240 #: PostgreSQLEnhanced/postgresqlenhanced.py:1256 msgid "Enhanced queries require JSONB support" -msgstr "" +msgstr "Gelişmiş sorgular JSONB desteği gerektirir" #: PostgreSQLEnhanced/postgresqlenhanced.py:1207 msgid "Full-text search requires JSONB support or search capabilities" -msgstr "" +msgstr "Tam metin araması için JSONB desteği veya arama yetenekleri gereklidir" #: PostgreSQLEnhanced/queries.py:364 msgid "pg_trgm extension required for duplicate detection" -msgstr "" +msgstr "Yinelenen kopyaların tespiti için pg_trgm uzantısı gereklidir" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py:29 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py:37 msgid "Prerequisites Checker" -msgstr "" +msgstr "Önkoşullar Denetleyicisi" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.gpr.py:30 msgid "Prerequisites Checker Gramplet" -msgstr "" +msgstr "Önkoşul Denetleyicisi Gramplet" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:144 msgid "" @@ -20877,73 +21231,96 @@ msgid "" "\">Gramps has all prerequisites installed.\n" msgstr "" +"Gramps'ın tüm önkoşullarının " +"yüklü olup olmadığını değerlendirmeye yardımcı olan tanılama Gramplet'i.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:153 msgid "" "Diagnostic Gramplet to help evaluate if Gramps has all prerequisites " "installed." msgstr "" +"Gramps'in gerekli tüm önkoşullarının yüklü olup olmadığını değerlendirmeye " +"yardımcı olan Tanılama Gramplet'i." #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:191 msgid "" "\n" "REQUIRED\n" msgstr "" +"\n" +"ZORUNLU\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:194 msgid "" "Installations of the following packages are ABSOLUTELY REQUIRED\n" " (Requires the minimum version or greater.):\n" msgstr "" +"Aşağıdaki paketlerin kurulumu KESİNLİKLE GEREKLİDİR\n" +" (En az belirtilen sürüm veya daha yenisini gerektirir.):\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:213 msgid "" "\n" "RECOMMENDED\n" msgstr "" +"\n" +"ÖNERİLEN\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:216 msgid "" "Installations of the following packages are STRONGLY RECOMMENDED " "as necessary for Geography and Charts:\n" msgstr "" +"Coğrafya ve Haritalar için gerekli olduğu durumlarda aşağıdaki paketlerin " +"yüklenmesi ŞİDDETLE ÖNERİLİR :\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:231 msgid "" "\n" "Optional\n" msgstr "" +"\n" +"İsteğe bağlı\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:234 msgid "Installations of the following packages are optional:\n" -msgstr "" +msgstr "Aşağıdaki paketlerin yüklemeleri isteğe bağlıdır:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:239 msgid "Gtkspell enables spell checking in the notes.\n" -msgstr "" +msgstr "Gtkspell notlarda yazım denetimi özelliğini etkinleştirir.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:245 msgid "" "Python Image Library (PIL) is needed for cropping images and LaTeX output." msgstr "" +"Resim kırpma ve LaTeX çıktısı için Python Resim Kütüphanesi (PIL) gereklidir." #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:261 msgid "" "\n" "Development & Translation Requirements\n" msgstr "" +"\n" +"Geliştirme ve Çeviri Gereksinimleri\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:265 msgid "" "Installations of the following packages are RECOMMENDED if you " "intend to translate or do any development (addons etc.):\n" msgstr "" +"Çeviri yapmayı veya herhangi bir geliştirme (eklenti vb.) yapmayı " +"planlıyorsanız aşağıdaki paketlerin yüklemeleri ÖNERİLİR:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:277 msgid "" "\n" "Optional packages required by Third-party Addons\n" msgstr "" +"\n" +"Üçüncü taraf eklentiler tarafından gerekli olan isteğe bağlı paketler\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:281 msgid "" @@ -20951,22 +21328,29 @@ msgid "" "project.org/wiki/index.php?title=Third-party_Addons\">Third-party Addons " "to work:\n" msgstr "" +"Aşağıdaki Üçüncü Taraf Eklentilerinin çalışması için gerekli ön " +"koşullar:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:305 msgid "" "\n" "Diagnostic checks\n" msgstr "" +"\n" +"Teşhis kontrolleri\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:306 msgid "Check for potential issues.\n" -msgstr "" +msgstr "Olası sorunları kontrol edin.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:307 msgid "" "\n" "Environment settings:\n" msgstr "" +"\n" +"Ortam ayarları:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:312 msgid "" @@ -20974,12 +21358,17 @@ msgid "" "\n" "Locales available:\n" msgstr "" +"\n" +"\n" +"Kullanılabilir yerel ayarlar:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:316 msgid "" "\n" "Back Up Your Genealogy Files.\n" msgstr "" +"\n" +"Soy ağacı dosyalarınızı yedekleyin.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:319 msgid "" @@ -20987,6 +21376,10 @@ msgid "" "back up your genealogy files, and then test your backups!\n" "\n" msgstr "" +"Önkoşulları kontrol etmeniz için bir nedeniniz varsa, soy ağacı " +"dosyalarınızın yedeğini almanın ve ardından yedeklerinizi test etmenin tam " +"zamanı!\n" +"\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:326 msgid "" @@ -21010,6 +21403,8 @@ msgid "" " • Backups can be made at any time and, at a minimum, on the first day of " "every month. But preferrably more often.\n" msgstr "" +" • Yedeklemeler her zaman yapılabilir ve en azından her ayın ilk günü " +"yapılmalıdır. Ancak daha sık yapılması tercih edilir.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:342 msgid "" @@ -21029,7 +21424,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:370 msgid ", is the most current version.\n" -msgstr "" +msgstr ", bu en güncel sürümdür.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:392 #, python-format @@ -21037,6 +21432,9 @@ msgid "" "You have Gramps %s. Please make a backup and then upgrade.\n" "%s" msgstr "" +"Gramps %s'ye sahipsiniz. Lütfen yedekleme yapın ve ardından yükseltme " +"işlemini gerçekleştirin.\n" +"%s" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:397 #, python-format @@ -21045,11 +21443,16 @@ msgid "" "for contributing and testing; please report any issues. Backups are your " "friend.\n" msgstr "" +"Gramps %s adlı, henüz yayınlanmamış bir geliştirme sürümünü yüklediniz. " +"Katkılarınız ve testleriniz için teşekkür ederiz; lütfen herhangi bir sorun " +"bildiriniz. Yedeklemeler her zaman işinize yarayacaktır.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:407 #, python-format msgid "You have Gramps %s. Congratulations, you have the current version.\n" msgstr "" +"%s sürümündeki Gramps'a sahipsiniz. Tebrikler, en güncel sürümü " +"kullanıyorsunuz.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:435 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:465 @@ -21061,7 +21464,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 msgid " (Requires version " -msgstr "" +msgstr " (Yüklemesi gereken sürüm " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:436 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:444 @@ -21073,7 +21476,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:693 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:740 msgid " or greater installed.)\n" -msgstr "" +msgstr " veya daha üst bir sürüm gerektirir.)\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:443 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:473 @@ -21086,7 +21489,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 msgid " (Passed: version " -msgstr "" +msgstr " (Geçti: sürüm " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:505 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:581 @@ -21098,11 +21501,11 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 msgid "unknown version" -msgstr "" +msgstr "bilinmeyen sürüm" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:513 msgid "DISPLAY not set" -msgstr "" +msgstr "ayarlanmamış veya" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:548 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:610 @@ -21110,33 +21513,35 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:684 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:731 msgid " or greater.)\n" -msgstr "" +msgstr " daha yüksek.)\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:682 msgid " (Requires " -msgstr "" +msgstr " (Gereklidir " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 msgid " • Berkeley Database library (bsddb3: " -msgstr "" +msgstr " • Berkeley Veritabanı kütüphanesi (bsddb3: " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 msgid " • SQLite Database library (sqlite3: " -msgstr "" +msgstr " • SQLite Veritabanı kütüphanesi (sqlite3: " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 msgid " • xdg-utils (Manual check see instructions link)" -msgstr "" +msgstr " • xdg-utils (Manuel kontrol için talimatlar bağlantısına bakın)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 msgid " • librsvg2 (Manual check see instructions link)" -msgstr "" +msgstr " • librsvg2 (Manuel kontrol için talimatlar bağlantısına bakın)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" +" • language-pack-gnome-xx (Manuel kontrol, talimatlar bağlantısına bakın) " +"Diliniz için " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 @@ -21145,40 +21550,40 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 msgid " or greater installed.)" -msgstr "" +msgstr " veya daha üstü yüklü.)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 msgid " or greater)" -msgstr "" +msgstr " veya daha büyük)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 msgid "Graphviz not in system PATH" -msgstr "" +msgstr "Graphviz sistem YOLUNDA değil" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 msgid "Ghostscript not in system PATH" -msgstr "" +msgstr "Ghostscript sistem YOLUNDA değil" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 msgid " (Passed: version 0.5.x is installed.)" -msgstr "" +msgstr " (Başarılı: 0.5.x sürümü yüklü.)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 msgid " (Requires version 0.5.x)" -msgstr "" +msgstr " (0.5.x sürümü gereklidir)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 msgid " • python-fontconfig not found, (Requires version 0.5.x)" -msgstr "" +msgstr " • python-fontconfig bulunamadı, (0.5.x sürümü gereklidir)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 msgid " • python-fontconfig installed, version unavailable" -msgstr "" +msgstr " • python-fontconfig yüklü, sürüm bilgisi mevcut değil" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 msgid " or greater installed.) (enchant module: " -msgstr "" +msgstr " veya daha üstü kurulu.) (büyü modülü: " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 #, python-format @@ -21186,6 +21591,8 @@ msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" +" • rcs %s TBD (Geçti: %s veya daha üstü sürüm yüklü. Microsoft Windows'ta " +"değilse)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 #, python-format @@ -21193,52 +21600,57 @@ msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" +" • rcs %s TBD (Microsoft Windows'da yüklü değilse, %s veya daha üst bir " +"sürümün kurulu olması gerekir)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." -msgstr "" +msgstr "exiv2 yürütülebilir dosyası yüklü değil, libexiv2 sürümü alınamıyor." #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" -msgstr "" +msgstr "GExiv2 : %s (Exiv2 kütüphanesi : %s)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 msgid "found another font" -msgstr "" +msgstr "başka bir yazı tipi bulundu" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" +"Networkchart eklentisi için White Rabbit yazı tipi son derece okunaklı bir sonuç " +"sağlar.\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 msgid "not installed" -msgstr "" +msgstr "yüklenmedi" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 msgid " and one of either: (pydotplus: " -msgstr "" +msgstr " ve bunlardan biri: (pydotplus: " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid ") or (pygraphviz: " -msgstr "" +msgstr ") veya (pygraphviz: " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 msgid "Installed(MS-Windows)" -msgstr "" +msgstr "Yüklü (MS-Windows)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 msgid "Installed(Linux/Mac)" -msgstr "" +msgstr "Yüklü (Linux/Mac)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 msgid "not installed " -msgstr "" +msgstr "yüklenmedi " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 @@ -21247,45 +21659,47 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 msgid "not found." -msgstr "" +msgstr "bulunamadı." #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" +"Standart. Geçti: program yüklü - 64 bit Windows işletim sisteminde 32 bit." #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" +") ('Eklenti kütüphanesi' altında listelenen gramps eklentisini gerektirir)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 msgid "DBF installed" -msgstr "" +msgstr "DBF yüklü" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 msgid ".)(Requires version " -msgstr "" +msgstr ".)(Sürüm gerektirir " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 msgid " installed.)(Passed: version " -msgstr "" +msgstr " yüklü.)(Geçti: sürüm " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 #, python-format msgid "(OpenCV facedetection: %s)" -msgstr "" +msgstr "(OpenCV yüz algılama: %s)" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 msgid " (lxml: not found. Requires version " -msgstr "" +msgstr " (lxml: bulunamadı. Sürüm gerektirir " #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 msgid " • Requires: MongoDB TBD / pymongo TBD" -msgstr "" +msgstr " • Gerektirir: MongoDB TBD / pymongo TBD" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 #, python-format msgid " • Operating System: %s" -msgstr "" +msgstr " • İşletim Sistemi: %s" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 msgid "" @@ -21294,6 +21708,10 @@ msgid "" "_Command_Line#LANG.2C_LANGUAGE.2C_LC_MESSAGE.2C_LC_TIME\">Locale Settings:\n" msgstr "" +"Yerel Ayarlar:" +"\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 @@ -21304,7 +21722,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 msgid "not set" -msgstr "" +msgstr "ayarlanmadı" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 @@ -21315,7 +21733,7 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 msgid "not tested" -msgstr "" +msgstr "test edilmedi" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 msgid "" @@ -21324,74 +21742,83 @@ msgid "" "Gramps again and make sure to select all the Translations and Dictionaries)\n" "\n" msgstr "" +"\n" +"Yüklü Yerel Ayarlar\\Çeviriler (Yalnızca İngilizce listeleniyorsa lütfen " +"Gramps'i yeniden yükleyin ve tüm Çeviriler ile Sözlükleri seçtiğinizden emin " +"olun)\n" +"\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" +"\n" +"Gramps Ortam değişkenleri:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 msgid "found" -msgstr "" +msgstr "bulundu" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" msgstr "" +"Sphinx, Gramps geliştirme belgelerini ve kılavuz sayfalarını oluşturan bir " +"araçtır\n" #: PythonGramplet/PythonGramplet.gpr.py:4 #: PythonGramplet/PythonGramplet.gpr.py:11 msgid "Python Shell" -msgstr "" +msgstr "Python Kabuğu" #: PythonGramplet/PythonGramplet.gpr.py:5 msgid "Interactive Python interpreter" -msgstr "" +msgstr "Etkileşimli Python yorumlayıcısı" #: PythonGramplet/PythonGramplet.py:55 Query/QueryGramplet.py:52 msgid "Enter Python expressions" -msgstr "" +msgstr "Python ifadelerini girin" #: Query/Query.gpr.py:10 msgid "Query Gramplet" -msgstr "" +msgstr "Gramplet Sorgusu" #: Query/Query.gpr.py:11 msgid "Gramplet for running SQL-like queries" -msgstr "" +msgstr "SQL benzeri sorgular çalıştırmak için Gramplet" #: Query/Query.gpr.py:19 msgid "Query" -msgstr "" +msgstr "Sorgu" #: Query/Query.gpr.py:33 msgid "Query Quickview" -msgstr "" +msgstr "Sorgu Hızlı Görünümü" #: Query/Query.gpr.py:34 msgid "Quick view for SQL-like running queries" -msgstr "" +msgstr "SQL benzeri çalışan sorgular için hızlı görünüm" #: Query/QueryGramplet.py:176 msgid "Enter SQL query" -msgstr "" +msgstr "SQL sorgusu girin" #: Query/QueryQuickview.py:347 Query/QueryQuickview.py:367 #, python-brace-format msgid "{rows:d} rows processed in {secs} seconds.\n" -msgstr "" +msgstr "{rows:d} satır {secs} saniyede işlendi.\n" #: Query/QueryQuickview.py:426 TimelineQuickview/TimelineQuickview.py:186 msgid "Today" -msgstr "" +msgstr "Bugün" #: QuiltView/QuiltView.gpr.py:23 QuiltView/QuiltView.gpr.py:34 msgid "Quilt Chart" -msgstr "" +msgstr "Yorgan Grafiği" #: QuiltView/QuiltView.gpr.py:25 msgid "The view shows a quilt chart visualisation of a family tree" @@ -21401,19 +21828,19 @@ msgstr "" #: QuiltView/QuiltView.py:223 msgid "Quilt chart" -msgstr "" +msgstr "Yorgan grafiği" #: QuiltView/QuiltView.py:307 msgid "Select the name for which you want to see." -msgstr "" +msgstr "Görmek istediğiniz adı seçin." #: QuiltView/QuiltView.py:314 msgid "Clear the entry field in the name selection box." -msgstr "" +msgstr "Ad seçimi kutusundaki giriş alanını temizleyin." #: QuiltView/QuiltView.py:319 msgid "Nothing is selected" -msgstr "" +msgstr "Hiçbir şey seçilmedi" #: QuiltView/QuiltView.py:572 #, python-format @@ -21421,86 +21848,90 @@ msgid "" "You have %(filter)d filtered people, %(count)d people shown on the tree and " "%(total)d people in your database." msgstr "" +"%(filter)d filtrelenmiş kişi var, ağaçta %(count)d kişi gösteriliyor ve " +"veritabanınızda %(total)d kişi var." #: QuiltView/QuiltView.py:662 msgid "Loading individuals" -msgstr "" +msgstr "Bireyler yükleniyor" #: QuiltView/QuiltView.py:663 msgid "Loading the data" -msgstr "" +msgstr "Veriler yükleniyor" #: QuiltView/QuiltView.py:1053 msgid "About Quilt View" -msgstr "" +msgstr "Quilt Görünümü Hakkında" #: QuiltView/QuiltView.py:1191 QuiltView/QuiltView.py:1265 msgid "Edit tags" -msgstr "" +msgstr "Etiketleri düzenle" #: QuiltView/QuiltView.py:1810 msgid "Printing the tree" -msgstr "" +msgstr "Ağacı yazdırma" #: QuiltView/QuiltView.py:1814 #, python-format msgid "Need to print %(pages)s pages (%(format)s format)" -msgstr "" +msgstr "%(pages)s sayfa yazdırılması gerekiyor (%(format)s biçimi)" #: QuiltView/QuiltView.py:1866 msgid "" "Center on the selected person.\n" "The new position of the person will be near the top left corner." msgstr "" +"Seçilen kişiyi ortala.\n" +"Kişinin yeni konumu sol üst köşeye yakın olacaktır." #: QuiltView/QuiltView.py:1871 msgid "The path color" -msgstr "" +msgstr "Yol rengi" #: QuiltView/QuiltView.py:1876 msgid "The selected person color" -msgstr "" +msgstr "Seçilen kişi rengi" #: QuiltView/QuiltView.py:1881 msgid "The path transparency" -msgstr "" +msgstr "Yol şeffaflığı" #: QuiltView/QuiltView.py:1884 RemoveTagTool/RemoveTagTool.py:89 #: RemoveTagTool/RemoveTagTool.py:97 RemoveTagTool/RemoveTagTool.py:105 msgid "General options" -msgstr "" +msgstr "Genel seçenekler" #: RUWebConnectPack/RUWebPack.gpr.py:11 msgid "RU Web Connect Pack" -msgstr "" +msgstr "RU Web Bağlantı Paketi" #: RUWebConnectPack/RUWebPack.gpr.py:12 msgid "Collection of Web sites for the RU (requires libwebconnect)" -msgstr "" +msgstr "RU için web siteleri koleksiyonu (libwebconnect gerektirir)" #: RUWebConnectPack/RUWebPack.py:40 msgid "OBD \"Memorial\"" -msgstr "" +msgstr "OBD \"Anıt\"" #: RUWebConnectPack/RUWebPack.py:41 msgid "People memory" -msgstr "" +msgstr "Kişi hafızası" #: RUWebConnectPack/RUWebPack.py:42 msgid "Winners" -msgstr "" +msgstr "Kazananlar" #: RUWebConnectPack/RUWebPack.py:43 msgid "Lived" -msgstr "" +msgstr "Yaşayanlar" #: RUWebConnectPack/RUWebPack.py:44 msgid "Open list" -msgstr "" +msgstr "Listeyi aç" #: RUWebConnectPack/RUWebPack.py:45 msgid "Wait for me" -msgstr "" +msgstr "Beni bekle" #: RUWebConnectPack/RUWebPack.py:46 msgid "All Russia Family Tree (forum)" @@ -21508,31 +21939,31 @@ msgstr "Tüm Rusya Aile Ağacı (forum)" #: RUWebConnectPack/RUWebPack.py:47 msgid "Yandex people" -msgstr "" +msgstr "Yandex kişileri" #: RUWebConnectPack/RUWebPack.py:49 msgid "RIA Officers" -msgstr "" +msgstr "RIA Görevlileri" #: RUWebConnectPack/RUWebPack.py:50 msgid "FamilySpace" -msgstr "" +msgstr "Aile Alanı" #: RelID/relation_tab.gpr.py:36 msgid "Display relations and distances with the home person" -msgstr "" +msgstr "Ana kişiyle olan ilişkileri ve mesafeleri göster" #: RelID/relation_tab.gpr.py:37 msgid "Will display relational informations with the home person" -msgstr "" +msgstr "Ana kişiyle olan ilişkisel bilgileri görüntüler" #: RelID/relation_tab.py:297 msgid "Relation and distances with root" -msgstr "" +msgstr "Kök kişiyle olan ilişki ve mesafeler" #: RelID/relation_tab.py:315 msgid "Filter rule" -msgstr "" +msgstr "Filtre kuralı" #: RelID/relation_tab.py:319 msgid "Select the filter rule" @@ -21540,370 +21971,377 @@ msgstr "Filtre kuralını seçin" #: RelID/relation_tab.py:321 msgid "Deep generations" -msgstr "" +msgstr "Derin nesiller" #: RelID/relation_tab.py:322 msgid "How deep should we go?" -msgstr "" +msgstr "Ne kadar derine gitmeliyiz?" #: RelID/relation_tab.py:339 RelID/relation_tab.py:601 msgid "Rel_id" -msgstr "" +msgstr "İlişki_Kimliği" #: RelID/relation_tab.py:342 msgid "up" -msgstr "" +msgstr "yukarı" #: RelID/relation_tab.py:343 msgid "down" -msgstr "" +msgstr "aşağı" #: RelID/relation_tab.py:344 msgid "Common MRA" -msgstr "" +msgstr "Ortak MRA" #: RelID/relation_tab.py:346 RelID/relation_tab.py:601 msgid "Period" -msgstr "" +msgstr "Dönem" #: RelID/relation_tab.py:350 RelID/relation_tab.py:603 msgid "Shared Subtree" -msgstr "" +msgstr "Paylaşılan Alt Ağaç" #: RelID/relation_tab.py:351 RelID/relation_tab.py:603 msgid "Centrality" -msgstr "" +msgstr "Merkezilik" #: RelID/relation_tab.py:352 RelID/relation_tab.py:603 msgid "Unique Ancestors" -msgstr "" +msgstr "Benzersiz Atalar" #: RelID/relation_tab.py:353 RelID/relation_tab.py:603 msgid "Surname Diversity" -msgstr "" +msgstr "Soyadı Çeşitliliği" #: RelID/relation_tab.py:367 msgid "Quit" -msgstr "" +msgstr "Çıkış" #: RelID/relation_tab.py:376 msgid "No default person set." -msgstr "" +msgstr "Varsayılan kişi ayarlanmadı." #: RelID/relation_tab.py:387 msgid "Please wait, filtering..." -msgstr "" +msgstr "Lütfen bekleyin, filtreleniyor..." #: RelID/relation_tab.py:446 msgid "Network Metrics" -msgstr "" +msgstr "Ağ Ölçümleri" #: RelID/relation_tab.py:447 msgid "Enabled" -msgstr "" +msgstr "Etkin" #: RelID/relation_tab.py:449 msgid "Enable or disable family network metrics" -msgstr "" +msgstr "Aile ağı ölçümlerini etkinleştir veya devre dışı bırak" #: RelID/relation_tab.py:483 msgid "Generating relation map..." -msgstr "" +msgstr "İlişki haritası oluşturuluyor..." #: RelID/relation_tab.py:497 msgid "" "\n" "Filtering\tCurrent match\t\tTime of pass\n" msgstr "" +"\n" +"Filtreleme\tGeçerli eşleşme\t\tGeçiş süresi\n" #: RelID/relation_tab.py:498 #, python-format msgid "%d/%d \t\t %d/%d \t\t%f" -msgstr "" +msgstr "%d/%d \t\t %d/%d \t\t%f" #: RelID/relation_tab.py:628 msgid "Folder Chooser" -msgstr "" +msgstr "Klasör Seçici" #: RelID/relation_tab.py:636 msgid "Please, select a folder" -msgstr "" +msgstr "Lütfen, bir klasör seçin" #: RelID/relation_tab.py:643 msgid "Foldername need" -msgstr "" +msgstr "Klasör adı gerekli" #: RelID/relation_tab.py:643 msgid "Foldername will be used for saving the content." -msgstr "" +msgstr "Klasör adı içeriği kaydetmek için kullanılacaktır." #: RelID/relation_tab.py:655 msgid "Cannot set a valid location." -msgstr "" +msgstr "Geçerli bir konum ayarlanamıyor." #: RelID/relation_tab.py:655 msgid "Did you set a foldername?" -msgstr "" +msgstr "Bir klasör adı belirledin mi?" #: RelID/relation_tab.py:676 msgid "Failed to save data." -msgstr "" +msgstr "Veriler kaydedilemedi." #: RelID/relation_tab.py:689 msgid "Relationships Map and Tab" -msgstr "" +msgstr "İlişkiler Haritası ve Sekmesi" #: RelID/relation_tab.py:772 msgid "Enable family network metrics" -msgstr "" +msgstr "Aile ağı ölçümlerini etkinleştirin" #: RelID/relation_tab.py:774 msgid "Whether to calculate and display family network metrics." msgstr "" +"Aile ağı ölçümlerinin hesaplanıp hesaplanmayacağı ve görüntülenip " +"görüntülenmeyeceği." #: RelatedRelativesGramplet/RelatedRelativesGramplet.gpr.py:4 #: RelatedRelativesGramplet/RelatedRelativesGramplet.gpr.py:11 msgid "Related Relatives" -msgstr "" +msgstr "İlgili Akrabalar" #: RelatedRelativesGramplet/RelatedRelativesGramplet.gpr.py:5 msgid "Gramplet showing relatives in a relation" -msgstr "" +msgstr "Bir ilişkide akrabaları gösteren Gramplet" #: RelatedRelativesGramplet/RelatedRelativesGramplet.py:93 msgid "Relations of related people in your database:" -msgstr "" +msgstr "Veritabanınızdaki ilgili kişilerin ilişkileri:" #: RelatedRelativesGramplet/RelatedRelativesGramplet.py:219 msgid "are partners and" -msgstr "" +msgstr "partnerdirler ve" #: RelatedRelativesGramplet/RelatedRelativesGramplet.py:242 msgid "Common ancestor" -msgstr "" +msgstr "Ortak ata" #: RelatedRelativesGramplet/RelatedRelativesGramplet.py:253 msgid "No relatives in a relation found" -msgstr "" +msgstr "İlişkide hiçbir akraba bulunamadı" #: RelatedRelativesGramplet/RelatedRelativesGramplet.py:254 msgid "END" -msgstr "" +msgstr "SON" #: RemoveTagTool/RemoveTagTool.gpr.py:25 RemoveTagTool/RemoveTagTool.py:186 #: RemoveTagTool/RemoveTagTool.py:246 msgid "Add/Remove Tag Tool" -msgstr "" +msgstr "Etiket Ekle/Kaldır Aracı" #: RemoveTagTool/RemoveTagTool.gpr.py:26 msgid "Add or remove a tag from groups of people, events, etc." -msgstr "" +msgstr "İnsan gruplarına, etkinliklere vb. bir etiket ekleyin veya kaldırın." #: RemoveTagTool/RemoveTagTool.py:58 msgid "The Tool requires at least one tag to execute." -msgstr "" +msgstr "Aracın çalıştırılması için en az bir etiket gerekir." #: RemoveTagTool/RemoveTagTool.py:59 RemoveTagTool/RemoveTagTool.py:61 msgid "ERROR" -msgstr "" +msgstr "HATA" #: RemoveTagTool/RemoveTagTool.py:86 msgid "Add/Remove" -msgstr "" +msgstr "Ekle/Kaldır" #: RemoveTagTool/RemoveTagTool.py:87 msgid "Add or remove tags from objects." -msgstr "" +msgstr "Nesnelere etiket ekleyin veya kaldırın." #: RemoveTagTool/RemoveTagTool.py:88 msgid "Add Tags" -msgstr "" +msgstr "Etiketleri Ekle" #: RemoveTagTool/RemoveTagTool.py:88 msgid "Remove Tags" -msgstr "" +msgstr "Etiketleri Kaldır" #: RemoveTagTool/RemoveTagTool.py:96 msgid "Choose a category." -msgstr "" +msgstr "Bir kategori seçin." #: RemoveTagTool/RemoveTagTool.py:103 msgid "Choose Tag" -msgstr "" +msgstr "Etiket Seç" #: RemoveTagTool/RemoveTagTool.py:104 msgid "Choose a tag to remove." -msgstr "" +msgstr "Kaldırılacak etiketi seçin." #: RemoveTagTool/RemoveTagTool.py:140 msgid "Filter options" -msgstr "" +msgstr "Filtreleme seçenekleri" #: RemoveTagTool/RemoveTagTool.py:151 #, python-format msgid "All %s" -msgstr "" +msgstr "Tüm %s" #: RemoveTagTool/RemoveTagTool.py:198 msgid "" "Unable to run the tool. Please check if your database contains tags, filters " "and objects." msgstr "" +"Aracı çalıştıramadı. Lütfen veritabanınızda etiketler, filtreler ve nesneler " +"bulunup bulunmadığını kontrol edin." #: RemoveTagTool/RemoveTagTool.py:200 RemoveTagTool/RemoveTagTool.py:235 msgid "WARNING" -msgstr "" +msgstr "UYARI" #: RemoveTagTool/RemoveTagTool.py:234 #, python-format msgid "No %s objects were found in database." -msgstr "" +msgstr "Veritabanında hiçbir %s nesnesi bulunamadı." #: RemoveTagTool/RemoveTagTool.py:249 msgid "Process tags..." -msgstr "" +msgstr "Etiketleri işleyin..." #: RemoveTagTool/RemoveTagTool.py:266 msgid "added" -msgstr "" +msgstr "eklendi" #: RemoveTagTool/RemoveTagTool.py:268 msgid "removed" -msgstr "" +msgstr "kaldırıldı" #: RemoveTagTool/RemoveTagTool.py:269 msgid "Tag '{}' was {} to {} {} objects.\n" -msgstr "" +msgstr "'{}' etiketi, {} {} nesneleri için {} idi.\n" #: RepositoriesReport/RepositoriesReport.gpr.py:26 msgid "Repositories Report Options" -msgstr "" +msgstr "Depolar Rapor Seçenekleri" #: RepositoriesReport/RepositoriesReport.gpr.py:27 #: RepositoriesReport/RepositoriesReport.gpr.py:46 msgid "Produces a textual repositories report" -msgstr "" +msgstr "Metin biçiminde depolar raporu üretir" #: RepositoriesReport/RepositoriesReport.gpr.py:45 #: RepositoriesReport/RepositoriesReport.py:80 #: RepositoriesReport/RepositoriesReportAlt.py:139 msgid "Repositories Report" -msgstr "" +msgstr "Depolar Raporu" #: RepositoriesReport/RepositoriesReport.py:189 #: RepositoriesReport/RepositoriesReportAlt.py:523 msgid "The style used for repository title." -msgstr "" +msgstr "Depo başlığı için kullanılan stil." #: RepositoriesReport/RepositoriesReport.py:203 #: RepositoriesReport/RepositoriesReportAlt.py:538 #: SourcesCitationsReport/SourcesCitationsReport.py:591 msgid "The style used for each section." -msgstr "" +msgstr "Her bölüm için kullanılan stil." #: RepositoriesReport/RepositoriesReportAlt.py:197 msgid "Internet:" -msgstr "" +msgstr "İnternet:" #: RepositoriesReport/RepositoriesReportAlt.py:277 msgid "Publication information:" -msgstr "" +msgstr "Yayın bilgileri:" #: RepositoriesReport/RepositoriesReportAlt.py:282 msgid "Data:" -msgstr "" +msgstr "Veri:" #: RepositoriesReport/RepositoriesReportAlt.py:383 msgid "Page:" -msgstr "" +msgstr "Sayfa:" #: RepositoriesReport/RepositoriesReportAlt.py:424 msgid "Selection with a filter" -msgstr "" +msgstr "Filtre ile seçim" #: RepositoriesReport/RepositoriesReportAlt.py:436 msgid "Include repository's urls" -msgstr "" +msgstr "Deponun url adreslerini dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:437 msgid "Whether to include urls on repository." -msgstr "" +msgstr "Depoya url adreslerin eklenip eklenmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:440 msgid "Include repository's address" -msgstr "" +msgstr "Deponun adresini dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:441 msgid "Whether to include addresses on repository." -msgstr "" +msgstr "Depoya adreslerin dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:444 msgid "Include source's author" -msgstr "" +msgstr "Kaynağın yazarını dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:445 msgid "Whether to include author." -msgstr "" +msgstr "Yazarın dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:448 msgid "Include source's abbreviation" -msgstr "" +msgstr "Kaynak kısaltmasını dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:449 msgid "Whether to include abbreviation." -msgstr "" +msgstr "Kısaltmanın dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:452 msgid "Include source's publication information" -msgstr "" +msgstr "Kaynağın yayın bilgilerini dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:453 msgid "Whether to include publication information." -msgstr "" +msgstr "Yayın bilgilerinin dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:456 msgid "Include source's data" -msgstr "" +msgstr "Kaynak verilerini dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:457 msgid "Whether to include keys and values." -msgstr "" +msgstr "Anahtar ve değerlerin dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:461 msgid "Whether to include notes on repositories and sources." -msgstr "" +msgstr "Depolar ve kaynaklar hakkında notların dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:465 msgid "Whether to include media." -msgstr "" +msgstr "Medyanın dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:468 msgid "Include citations" -msgstr "" +msgstr "Alıntıları dahil edin" #: RepositoriesReport/RepositoriesReportAlt.py:469 msgid "Whether to include citations on sources." -msgstr "" +msgstr "Kaynaklar üzerinde alıntıların dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:473 msgid "Whether to include repositories and sources marked as private." msgstr "" +"Özel olarak işaretlenmiş depoların ve kaynakların dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:476 msgid "Display empty values" -msgstr "" +msgstr "Boş değerleri görüntüle" #: RepositoriesReport/RepositoriesReportAlt.py:477 msgid "Whether to include key records with empty values." -msgstr "" +msgstr "Boş değerlere sahip anahtar kayıtların dahil edilip edilmeyeceği." #: RepositoriesReport/RepositoriesReportAlt.py:553 msgid "The style used for child section." -msgstr "" +msgstr "Çocuk bölümü için kullanılan stil." #: RestoreHist/restorehist.gpr.py:30 msgid "Restart where you were last working" -msgstr "" +msgstr "En son çalıştığın yerde yeniden başla" #: RestoreHist/restorehist.gpr.py:32 msgid "" @@ -21911,150 +22349,158 @@ msgid "" "was previously closed. It adds no new menus or Gramplets, but allows the " "last six objects visited to be found via the 'Go' menu." msgstr "" +"Bu eklenti, Gramps'in daha önce kapatıldığı aynı görünüm ve nesnede yeniden " +"başlatılmasını sağlar. Yeni menüler veya Gramplet'ler eklemez, ancak 'Git' " +"menüsü aracılığıyla son altı ziyaret edilen nesnenin bulunmasına olanak " +"tanır." #: S3MediaUploader/S3MediaUploader.gpr.py:28 msgid "S3 Media Uploader" -msgstr "" +msgstr "S3 Medya Yükleyici" #: S3MediaUploader/S3MediaUploader.gpr.py:38 msgid "" "Upload media files to S3 (or compatible) object-based storage via the " "command line." msgstr "" +"Komut satırı aracılığıyla medya dosyalarını S3 (veya uyumlu) nesne tabanlı " +"depolamaya yükleyin." #: SVWebconnectPack/SVWebPack.gpr.py:11 msgid "SV Web Connect Pack" -msgstr "" +msgstr "SV Web Bağlantı Paketi" #: SVWebconnectPack/SVWebPack.gpr.py:12 msgid "Collection of Web sites for Sweden (requires libwebconnect)" -msgstr "" +msgstr "İsveç için web siteleri koleksiyonu (libwebconnect gerektirir)" #: SVWebconnectPack/SVWebPack.py:33 USWebConnectPack/USWebPack.py:33 msgid "Find A Grave" -msgstr "" +msgstr "Find A Grave" #: SVWebconnectPack/SVWebPack.py:35 msgid "Google SV" -msgstr "" +msgstr "Google SV" #: SVWebconnectPack/SVWebPack.py:38 msgid "FamilySearch.org Tree" -msgstr "" +msgstr "FamilySearch.org Ağacı" #: SVWebconnectPack/SVWebPack.py:40 msgid "National archives of Sweden personsearch" -msgstr "" +msgstr "İsveç Ulusal Arşivleri kişi araması" #: SVWebconnectPack/SVWebPack.py:41 msgid "National archives of Sweden free search" -msgstr "" +msgstr "İsveç Ulusal Arşivleri ücretsiz arama" #: SVWebconnectPack/SVWebPack.py:42 msgid "Arkiv Digital estate records for Skåne" -msgstr "" +msgstr "Skåne için Arkiv Digital miras kayıtları" #: SVWebconnectPack/SVWebPack.py:43 msgid "Ancestry Search" -msgstr "" +msgstr "Ancestry Arama" #: SVWebconnectPack/SVWebPack.py:44 msgid "Find swedish portraits - Rötter.se" -msgstr "" +msgstr "İsveç portrelerini bulun - Rötter.se" #: SVWebconnectPack/SVWebPack.py:45 msgid "Gravar.se - find swedish graves" -msgstr "" +msgstr "Gravar.se - İsveç mezarlarını bulun" #: SVWebconnectPack/SVWebPack.py:46 msgid "National archives of Sweden search censuses" -msgstr "" +msgstr "İsveç Ulusal Arşivleri nüfus sayımı araması" #: SVWebconnectPack/SVWebPack.py:47 msgid "Arkiv Digital released prisoners" -msgstr "" +msgstr "Arkiv Digital serbest bırakılmış mahkûmlar" #: SVWebconnectPack/SVWebPack.py:48 msgid "Gravestone inventory - rötter.se" -msgstr "" +msgstr "Mezar taşı envanteri - rötter.se" #: SearchGramplet/SearchGramplet.gpr.py:10 msgid "Gramplet for search objects in database." -msgstr "" +msgstr "Veritabanındaki nesneleri aramak için Gramplet." #: SearchGramplet/SearchGramplet.py:104 msgid "Sort by object type" -msgstr "" +msgstr "Nesne türüne göre sırala" #: SearchGramplet/SearchGramplet.py:138 msgid "Menu to set search objects" -msgstr "" +msgstr "Aranacak nesneleri ayarlama menüsü" #: SearchGramplet/SearchGramplet.py:140 msgid "Select objects to search" -msgstr "" +msgstr "Aranacak nesneleri seçin" #: SearchGramplet/SearchGramplet.py:154 msgid "SearchGramplet configuration menu" -msgstr "" +msgstr "SearchGramplet yapılandırma menüsü" #: SearchGramplet/SearchGramplet.py:261 msgid "No matches..." -msgstr "" +msgstr "Eşleşme yok..." #: SearchGramplet/SearchGramplet.py:353 msgid "Start type to search" -msgstr "" +msgstr "Aranacak başlangıç türü" #: SearchGramplet/SearchGramplet.py:389 msgid "" "Start type to search objects.\n" "To show all objects enter only «*» symbol." msgstr "" +"Aranacak başlangıç türü nesneleri.\n" +"Tüm nesneleri göstermek için yalnızca «*» sembolünü girin." #: SearchGramplet/SearchGramplet.py:743 #, python-format msgid "Birth: %s" -msgstr "" +msgstr "Doğum: %s" #: SearchGramplet/SearchGramplet.py:747 #, python-format msgid "Death: %s" -msgstr "" +msgstr "Ölüm: %s" #: SearchGramplet/SearchGramplet.py:918 msgid "Set as Active person" -msgstr "" +msgstr "Etkin kişi olarak ayarla" #: SearchGramplet/SearchGramplet.py:920 msgid "Set as Home person" -msgstr "" +msgstr "Ana kişi olarak ayarla" #: SetAttributeTool/SetAttributeTool.gpr.py:26 #: SetAttributeTool/SetAttributeTool.py:122 #: SetAttributeTool/SetAttributeTool.py:150 msgid "Set Attribute" -msgstr "" +msgstr "Öznitelik Ayarla" #: SetAttributeTool/SetAttributeTool.gpr.py:27 msgid "Set an attribute to a given value." -msgstr "" +msgstr "Belirli bir değere sahip bir öznitelik ayarlayın." #: SetAttributeTool/SetAttributeTool.py:83 msgid "Attribute type to add or edit" -msgstr "" +msgstr "Eklenecek veya düzenlenecek öznitelik türü" #: SetAttributeTool/SetAttributeTool.py:84 msgid "Attribute value to add or edit" -msgstr "" +msgstr "Eklenecek veya düzenlenecek öznitelik değeri" #: SetAttributeTool/SetAttributeTool.py:92 msgid "Remove attribute type and value set" -msgstr "" +msgstr "Öznitelik türü ve değer kümesini kaldır" #: SetAttributeTool/SetAttributeTool.py:152 msgid "Setting attributes..." -msgstr "" +msgstr "Öznitelikler ayarlanıyor..." #: SetAttributeTool/SetAttributeTool.py:156 #, python-format @@ -22062,6 +22508,8 @@ msgid "" "Setting '%s' attributes to '%s'...\n" "\n" msgstr "" +"'%s' öznitelikleri '%s' olarak ayarlanıyor...\n" +"\n" #: SetAttributeTool/SetAttributeTool.py:188 #, python-format @@ -22069,14 +22517,16 @@ msgid "" "\n" "Set %d '%s' attributes to '%s'\n" msgstr "" +"\n" +"%d adet '%s' özniteliğini '%s' olarak ayarla\n" #: SetAttributeTool/SetAttributeTool.py:191 msgid "Remove Attribute" -msgstr "" +msgstr "Özniteliği Kaldır" #: SetAttributeTool/SetAttributeTool.py:194 msgid "Removing attributes..." -msgstr "" +msgstr "Öznitelikler kaldırılıyor..." #: SetAttributeTool/SetAttributeTool.py:221 #, python-format @@ -22084,122 +22534,128 @@ msgid "" "\n" "Removing %d '%s' attributes to '%s'\n" msgstr "" +"\n" +"%d adet '%s' özniteliği '%s' olarak kaldırılıyor\n" #: SetPrivacyTool/SetPrivacyTool.gpr.py:24 SetPrivacyTool/SetPrivacyTool.py:51 #: SetPrivacyTool/SetPrivacyTool.py:93 SetPrivacyTool/SetPrivacyTool.py:96 #: SetPrivacyTool/SetPrivacyTool.py:146 SetPrivacyTool/SetPrivacyTool.py:186 #: SetPrivacyTool/SetPrivacyTool.py:226 SetPrivacyTool/SetPrivacyTool.py:255 msgid "Set Privacy Tool" -msgstr "" +msgstr "Gizlilik Aracı Ayarla" #: SetPrivacyTool/SetPrivacyTool.gpr.py:25 msgid "Set all objects of the last of years private." -msgstr "" +msgstr "Son yıllık tüm nesneleri özel olarak ayarlayın." #: SetPrivacyTool/SetPrivacyTool.py:90 #, python-format msgid "Set private: %d %s\n" -msgstr "" +msgstr "Özel olarak ayarla: %d %s\n" #: SetPrivacyTool/SetPrivacyTool.py:92 #, python-format msgid "Not private: %d %s\n" -msgstr "" +msgstr "Özel değil: %d %s\n" #: SetPrivacyTool/SetPrivacyTool.py:107 msgid "Set persons private.." -msgstr "" +msgstr "Kişileri özel olarak ayarla.." #: SetPrivacyTool/SetPrivacyTool.py:157 msgid "Set events private.." -msgstr "" +msgstr "Etkinlikleri özel olarak ayarla.." #: SetPrivacyTool/SetPrivacyTool.py:197 msgid "Set media private.." -msgstr "" +msgstr "Medyayı özel olarak ayarla.." #: SetPrivacyTool/SetPrivacyTool.py:230 msgid "Set adresses private.." -msgstr "" +msgstr "Adresleri özel olarak ayarla.." #: SetPrivacyTool/SetPrivacyTool.py:259 msgid "Set internet objects private.." -msgstr "" +msgstr "İnternet nesnelerini özel olarak ayarla.." #: SetPrivacyTool/SetPrivacyTool.py:301 msgid "" "The time range in years from today you want to set objects private.\n" "'0 years' = remove privacy from all objects." msgstr "" +"Nesneleri özel olarak ayarlamak istediğiniz tarihten itibaren yıl cinsinden " +"zaman aralığı.\n" +"'0 yıl' = tüm nesnelerin gizliliğini kaldırır." #: SetPrivacyTool/SetPrivacyTool.py:306 msgid "Always private if no date." -msgstr "" +msgstr "Tarih yoksa her zaman özel." #: SetPrivacyTool/SetPrivacyTool.py:307 msgid "If checked, all objects without a date will also be set private." msgstr "" +"İşaretlenirse, tarihi olmayan tüm nesneler de özel olarak ayarlanacaktır." #: SetPrivacyTool/SetPrivacyTool.py:324 msgid "Select filter to restrict events" -msgstr "" +msgstr "Etkinlikleri kısıtlamak için filtre seçin" #: SetPrivacyTool/SetPrivacyTool.py:336 msgid "Select filter to restrict medias" -msgstr "" +msgstr "Medyaları kısıtlamak için filtre seçin" #: SetPrivacyTool/SetPrivacyTool.py:347 msgid "Internet type filter" -msgstr "" +msgstr "İnternet türü filtresi" #: SetPrivacyTool/SetPrivacyTool.py:351 msgid "E-Mail" -msgstr "" +msgstr "E-Posta" #: SharedPostgreSQL/sharedpostgresql.gpr.py:24 msgid "SharedPostgreSQL" -msgstr "" +msgstr "Paylaşılan PostgreSQL" #: SharedPostgreSQL/sharedpostgresql.gpr.py:25 msgid "Shared _PostgreSQL Database" -msgstr "" +msgstr "Paylaşılan _PostgreSQL Veritabanı" #: SharedPostgreSQL/sharedpostgresql.gpr.py:26 msgid "Shared PostgreSQL Database" -msgstr "" +msgstr "Paylaşılan PostgreSQL Veritabanı" #: SourceIndex/SourceIndex.gpr.py:24 SourceIndex/SourceIndex.gpr.py:25 msgid "BirthIndex" -msgstr "" +msgstr "Doğum Dizini" #: SourceIndex/SourceIndex.gpr.py:40 SourceIndex/SourceIndex.gpr.py:41 msgid "MarriageIndex" -msgstr "" +msgstr "Evlilik Dizini" #: SourceIndex/SourceIndex.gpr.py:56 SourceIndex/SourceIndex.gpr.py:57 msgid "DeathIndex" -msgstr "" +msgstr "Ölüm Dizini" #: SourceIndex/SourceIndex.gpr.py:72 SourceIndex/SourceIndex.gpr.py:73 msgid "CensusIndex" -msgstr "" +msgstr "Nüfus Sayımı Dizini" #: SourceIndex/SourceIndex.gpr.py:104 SourceIndex/SourceIndex.gpr.py:105 msgid "SourceIndex" -msgstr "" +msgstr "Kaynak Dizini" #: SourceIndex/birth.glade:7 SourceIndex/index.glade:585 msgid "birth" -msgstr "" +msgstr "doğum" #: SourceIndex/birth.glade:65 SourceIndex/census.glade:728 #: SourceIndex/death.glade:97 SourceIndex/marriage.glade:64 msgid "Info:" -msgstr "" +msgstr "Bilgi:" #: SourceIndex/birth.glade:99 msgid "Birth act:" -msgstr "" +msgstr "Doğum kaydı:" #: SourceIndex/birth.glade:129 msgid "" @@ -22220,59 +22676,65 @@ msgstr "Bu kaynağın cilt numarası veya sayfası" #: SourceIndex/birth.glade:195 SourceIndex/birth.glade:297 #: SourceIndex/birth.glade:644 SourceIndex/witness.glade:73 msgid "Names:" -msgstr "" +msgstr "Adlar:" #: SourceIndex/birth.glade:214 msgid "select the gender/sex" -msgstr "" +msgstr "Cinsi/cinsiyeti seçin" #: SourceIndex/birth.glade:247 msgid "The names of the main person written into the source" -msgstr "" +msgstr "Kaynakta adı geçen ana kişinin adı" #: SourceIndex/birth.glade:262 msgid "The first names of the main person written into the source" -msgstr "" +msgstr "Kaynakta yer alan ana kişinin ilk adları" #: SourceIndex/birth.glade:278 SourceIndex/birth.glade:511 #: SourceIndex/birth.glade:729 SourceIndex/witness.glade:90 msgid "First names:" -msgstr "" +msgstr "İlk adlar:" #: SourceIndex/birth.glade:334 SourceIndex/birth.glade:493 msgid "Other:" -msgstr "" +msgstr "Diğer:" #: SourceIndex/birth.glade:351 msgid "Repository title" -msgstr "" +msgstr "Depo başlığı" #: SourceIndex/birth.glade:388 SourceIndex/census.glade:973 #: SourceIndex/death.glade:589 SourceIndex/marriage.glade:234 msgid "Image" -msgstr "" +msgstr "Resim" #: SourceIndex/birth.glade:406 msgid "Repository" -msgstr "" +msgstr "Depo" #: SourceIndex/birth.glade:421 msgid "" "Spouse\n" "names:" msgstr "" +"Eş\n" +"adları:" #: SourceIndex/birth.glade:441 msgid "" "Marriage\n" "date:" msgstr "" +"Evlilik\n" +"tarihi:" #: SourceIndex/birth.glade:460 msgid "" "Death\n" "date:" msgstr "" +"Ölüm\n" +"tarihi:" #: SourceIndex/birth.glade:578 SourceIndex/birth.glade:677 #: SourceIndex/census.glade:543 SourceIndex/death.glade:722 @@ -22281,7 +22743,7 @@ msgstr "" #: SourceIndex/marriage.glade:941 SourceIndex/marriage.glade:1361 #: SourceIndex/marriage.glade:1474 SourceIndex/witness.glade:141 msgid "Occupation:" -msgstr "" +msgstr "Meslek:" #: SourceIndex/birth.glade:597 SourceIndex/birth.glade:710 #: SourceIndex/death.glade:741 SourceIndex/death.glade:854 @@ -22290,7 +22752,7 @@ msgstr "" #: SourceIndex/marriage.glade:1408 SourceIndex/marriage.glade:1455 #: SourceIndex/witness.glade:158 msgid "Live:" -msgstr "" +msgstr "Yaşıyor:" #: SourceIndex/birth.glade:833 msgid "" @@ -22298,30 +22760,33 @@ msgid "" "Godfather\n" "Godmother:
" msgstr "" +"Şahit\n" +"Vaftiz babası\n" +"Vaftiz annesi:" #: SourceIndex/birth.glade:870 msgid "Call number:" -msgstr "" +msgstr "Çağrı numarası:" #: SourceIndex/birth.glade:887 msgid "The call number into the repository" -msgstr "" +msgstr "Depoya çağrı numarası" #: SourceIndex/birth.glade:925 msgid "Data person" -msgstr "" +msgstr "Kişisel veriler" #: SourceIndex/birth.glade:949 msgid "Data margin" -msgstr "" +msgstr "Veri kenar boşluğu" #: SourceIndex/birth.glade:973 msgid "Data father" -msgstr "" +msgstr "Baba verileri" #: SourceIndex/birth.glade:985 msgid "Data mother" -msgstr "" +msgstr "Anne verileri" #: SourceIndex/birth.glade:1021 msgid "" @@ -22330,49 +22795,59 @@ msgid "" "godfather, name, given, age, occupation;\n" "etc ..." msgstr "" +"tanık1, ad, verilen ad, yaş, meslek;\n" +"tanık2, ad, verilen ad, yaş, meslek;\n" +"vaftiz babası, ad, verilen ad, yaş, meslek;\n" +"vb ..." #: SourceIndex/birth.glade:1549 msgid "" "Marriage\n" "place:" msgstr "" +"Evlilik\n" +"yeri:" #: SourceIndex/birth.glade:1568 msgid "" "Death\n" "place:" msgstr "" +"Ölüm\n" +"yeri:" #: SourceIndex/birth.glade:1588 msgid "" "Birth\n" "place:" msgstr "" +"Doğum\n" +"yeri:" #: SourceIndex/birth.glade:1892 SourceIndex/birth.glade:1911 msgid "Origine:" -msgstr "" +msgstr "Köken:" #: SourceIndex/birth.glade:2272 msgid "Citation" -msgstr "" +msgstr "Alıntı" #: SourceIndex/birth.glade:2287 msgid "Source" -msgstr "" +msgstr "Kaynak" #: SourceIndex/census.glade:7 SourceIndex/index.glade:768 msgid "census" -msgstr "" +msgstr "nüfus sayımı" #: SourceIndex/census.glade:160 msgid "Census" -msgstr "" +msgstr "Nüfus sayımı" #: SourceIndex/census.glade:176 SourceIndex/death.glade:607 #: SourceIndex/marriage.glade:185 msgid "Repository" -msgstr "" +msgstr "Depo" #: SourceIndex/census.glade:221 SourceIndex/death.glade:432 #: SourceIndex/death.glade:636 SourceIndex/death.glade:873 @@ -22380,19 +22855,19 @@ msgstr "" #: SourceIndex/marriage.glade:751 SourceIndex/marriage.glade:770 #: SourceIndex/marriage.glade:1543 SourceIndex/marriage.glade:1701 msgid "First name:" -msgstr "" +msgstr "İlk adı:" #: SourceIndex/census.glade:319 SourceIndex/death.glade:1003 msgid "Birth date:" -msgstr "" +msgstr "Doğum tarihi:" #: SourceIndex/census.glade:364 msgid "Death date:" -msgstr "" +msgstr "Ölüm tarihi:" #: SourceIndex/census.glade:381 SourceIndex/death.glade:1035 msgid "Birth place:" -msgstr "" +msgstr "Doğum yeri:" #: SourceIndex/census.glade:414 msgid "Death place:" @@ -22400,11 +22875,11 @@ msgstr "Ölüm yeri:" #: SourceIndex/census.glade:447 msgid "Family status:" -msgstr "" +msgstr "Aile durumu:" #: SourceIndex/census.glade:466 SourceIndex/death.glade:1099 msgid "Spouse name:" -msgstr "" +msgstr "Eşinin adı:" #: SourceIndex/census.glade:510 SourceIndex/death.glade:703 #: SourceIndex/death.glade:892 SourceIndex/marriage.glade:498 @@ -22412,16 +22887,16 @@ msgstr "" #: SourceIndex/marriage.glade:884 SourceIndex/marriage.glade:1526 #: SourceIndex/marriage.glade:1654 SourceIndex/witness.glade:124 msgid "Origin:" -msgstr "" +msgstr "Köken:" #: SourceIndex/census.glade:671 SourceIndex/death.glade:1067 #: WebSearch/markdown_place_history_formatter.py:59 msgid "Note:" -msgstr "" +msgstr "Not:" #: SourceIndex/census.glade:712 SourceIndex/death.glade:621 msgid "Data person" -msgstr "" +msgstr "Kişi verileri" #: SourceIndex/census.glade:904 SourceIndex/death.glade:217 #: SourceIndex/marriage.glade:1296 @@ -22430,309 +22905,321 @@ msgstr "Cilt:" #: SourceIndex/census.glade:1046 msgid "order, person, surname, given, age, status, occupation, etc ..." -msgstr "" +msgstr "sıra, kişi, soyadı, verilen ad, yaş, durum, meslek, vb ..." #: SourceIndex/death.glade:165 msgid "Death act:" -msgstr "" +msgstr "Ölüm kaydı:" #: SourceIndex/death.glade:282 msgid "Death act" -msgstr "" +msgstr "Ölüm kaydı" #: SourceIndex/death.glade:986 SourceIndex/marriage.glade:1720 msgid "Witness:" -msgstr "" +msgstr "Tanık:" #: SourceIndex/death.glade:1131 msgid "Marriage date:" -msgstr "" +msgstr "Evlilik tarihi:" #: SourceIndex/death.glade:1163 msgid "Marriage place:" -msgstr "" +msgstr "Evlilik yeri:" #: SourceIndex/death.glade:1195 msgid "Data mother" -msgstr "" +msgstr "Anne verileri" #: SourceIndex/death.glade:1210 msgid "Data father" -msgstr "" +msgstr "Baba verileri" #: SourceIndex/death.glade:1225 msgid "Spouse " -msgstr "" +msgstr " " #: SourceIndex/death.glade:1274 SourceIndex/death.glade:1293 #: SourceIndex/death.glade:1312 SourceIndex/marriage.glade:1819 #: SourceIndex/marriage.glade:1836 SourceIndex/marriage.glade:1855 #: SourceIndex/marriage.glade:1874 msgid "alive" -msgstr "" +msgstr "hayatta" #: SourceIndex/death.glade:1394 SourceIndex/marriage.glade:1773 msgid "" "witness1, name, given, age, occupation; witness2, name, given, age, " "occupation, etc ..." msgstr "" +"tanık1, ad, verilen ad, yaş, meslek; tanık2, ad, verilen ad, yaş, meslek, vb " +"..." #: SourceIndex/index.glade:7 msgid "index" -msgstr "" +msgstr "dizin" #: SourceIndex/index.glade:50 msgid "_File" -msgstr "" +msgstr "_Dosya" #: SourceIndex/index.glade:79 msgid "Import CSV" -msgstr "" +msgstr "CSV İçe Aktar" #: SourceIndex/index.glade:209 msgid "Display all" -msgstr "" +msgstr "Tümünü görüntüle" #: SourceIndex/index.glade:220 msgid "Display only births" -msgstr "" +msgstr "Yalnızca doğumları görüntüle" #: SourceIndex/index.glade:221 msgid "Births/Baptisms" -msgstr "" +msgstr "Doğumlar/Vaftizler" #: SourceIndex/index.glade:231 msgid "Display only marriages" -msgstr "" +msgstr "Yalnızca evlilikleri görüntüle" #: SourceIndex/index.glade:232 msgid "Marriages/Families" -msgstr "" +msgstr "Evlilikler/Aileler" #: SourceIndex/index.glade:242 msgid "Display only deaths" -msgstr "" +msgstr "Yalnızca ölümleri görüntüle" #: SourceIndex/index.glade:243 msgid "Deaths/Burials" -msgstr "" +msgstr "Ölümler/Definler" #: SourceIndex/index.glade:253 msgid "Display only censuses" -msgstr "" +msgstr "Yalnızca nüfus sayımlarını görüntüle" #: SourceIndex/index.glade:254 msgid "Censuses" -msgstr "" +msgstr "Nüfus sayımları" #: SourceIndex/index.glade:278 msgid "About this utility." -msgstr "" +msgstr "Bu yardımcı program hakkında." #: SourceIndex/index.glade:279 msgid "About dialog" -msgstr "" +msgstr "Hakkında iletişim kutusu" #: SourceIndex/index.glade:312 msgid "New birth or baptism" -msgstr "" +msgstr "Yeni doğum veya vaftiz" #: SourceIndex/index.glade:341 msgid "add birth act" -msgstr "" +msgstr "doğum kaydı ekle" #: SourceIndex/index.glade:373 msgid "New marriage" -msgstr "" +msgstr "Yeni evlilik" #: SourceIndex/index.glade:402 msgid "add marriage act" -msgstr "" +msgstr "evlilik kaydı ekle" #: SourceIndex/index.glade:434 msgid "New death or burial" -msgstr "" +msgstr "Yeni ölüm veya defin" #: SourceIndex/index.glade:463 msgid "add death act" -msgstr "" +msgstr "ölüm kaydı ekle" #: SourceIndex/index.glade:495 msgid "New census" -msgstr "" +msgstr "Yeni nüfus sayımı" #: SourceIndex/index.glade:524 msgid "add census" -msgstr "" +msgstr "nüfus sayımı ekle" #: SourceIndex/index.glade:556 msgid "Select a birth source" -msgstr "" +msgstr "Bir doğum kaynağı seçin" #: SourceIndex/index.glade:617 msgid "Select a marriage source" -msgstr "" +msgstr "Bir evlilik kaynağı seçin" #: SourceIndex/index.glade:678 msgid "Select a death source" -msgstr "" +msgstr "Bir ölüm kaynağı seçin" #: SourceIndex/index.glade:739 msgid "Select a census source" -msgstr "" +msgstr "Bir nüfus sayımı kaynağı seçin" #: SourceIndex/index.glade:800 msgid "Select a repository storing a source" -msgstr "" +msgstr "Bir kaynak saklayan depo seçin" #: SourceIndex/index.glade:829 msgid "repositories" -msgstr "" +msgstr "depolar" #: SourceIndex/index.glade:869 msgid "Selected view" -msgstr "" +msgstr "Seçilen görünüm" #: SourceIndex/index.glade:889 msgid "" "Records transcriptions ... births/deaths/marriages/censuses with common " "fields: id, repository, person name, birth date and place, parents." msgstr "" +"Kayıt dökümleri ... doğumlar/ölümler/evlilikler/nüfus sayımları için ortak " +"alanlar: kimlik, depo, kişi adı, doğum tarihi ve yeri, ebeveynler." #: SourceIndex/index.py:407 msgid "*** Error: Must install either ElementTree or lxml." -msgstr "" +msgstr "*** Hata: ElementTree veya lxml yüklenmelidir." #: SourceIndex/index.py:409 msgid "must install either ElementTree or lxml" -msgstr "" +msgstr "ElementTree veya lxml yüklenmelidir" #: SourceIndex/marriage.glade:49 msgid "Marriage act:" -msgstr "" +msgstr "Evlilik kaydı:" #: SourceIndex/marriage.glade:81 msgid "m. date:" -msgstr "" +msgstr "evlilik tarihi:" #: SourceIndex/marriage.glade:97 msgid "m. place:" -msgstr "" +msgstr "evlilik yeri:" #: SourceIndex/marriage.glade:113 msgid "d.contract:" -msgstr "" +msgstr "sözleşme tarihi:" #: SourceIndex/marriage.glade:131 msgid "dates banns:" -msgstr "" +msgstr "ilan tarihleri:" #: SourceIndex/marriage.glade:149 msgid "p.contract:" -msgstr "" +msgstr "sözleşme yeri:" #: SourceIndex/marriage.glade:167 msgid "places banns:" -msgstr "" +msgstr "ilan yerleri:" #: SourceIndex/marriage.glade:198 msgid "Data union" -msgstr "" +msgstr "Birlik verileri" #: SourceIndex/marriage.glade:318 msgid "Marriage act" -msgstr "" +msgstr "Evlilik kaydı" #: SourceIndex/marriage.glade:333 msgid "" "Data father\n" "spouse1" msgstr "" +"Baba verileri\n" +"eş1" #: SourceIndex/marriage.glade:351 msgid "" "Data mother\n" "spouse1" msgstr "" +"Anne verileri\n" +"eş1" #: SourceIndex/marriage.glade:369 msgid "" "Data mother\n" "spouse2" msgstr "" +"Anne verilei\n" +"eş2" #: SourceIndex/marriage.glade:387 msgid "" "Data father\n" "spouse2" msgstr "" +"Baba verileri\n" +"eş2" #: SourceIndex/marriage.glade:1185 msgid "Data spouse1" -msgstr "" +msgstr "Eş1 verileri" #: SourceIndex/marriage.glade:1202 msgid "Data spouse2" -msgstr "" +msgstr "Eş2 verileri" #: SourceIndex/witness.glade:7 msgid "witness" -msgstr "" +msgstr "tanık" #: SourceIndex/witness.glade:60 msgid "Witness" -msgstr "" +msgstr "Tanık" #: SourceReferences/SourceReferences.gpr.py:33 msgid "Gramplet showing the references for a source" -msgstr "" +msgstr "Bir kaynağın referanslarını gösteren Gramplet" #: SourcesCitationsReport/SourcesCitationsReport.gpr.py:32 msgid "Sources and Citations Report" -msgstr "" +msgstr "Kaynaklar ve Alıntılar Raporu" #: SourcesCitationsReport/SourcesCitationsReport.gpr.py:33 msgid "Provides a source and Citations Report with notes" -msgstr "" +msgstr "Notlarla birlikte bir Kaynak ve Alıntılar Raporu sağlar" #: SourcesCitationsReport/SourcesCitationsReport.py:272 #, python-format msgid "Key: %s" -msgstr "" +msgstr "Anahtar: %s" #: SourcesCitationsReport/SourcesCitationsReport.py:277 msgid "Citations:" -msgstr "" +msgstr "Alıntılar:" #: SourcesCitationsReport/SourcesCitationsReport.py:286 #, python-format msgid "%d" -msgstr "" +msgstr "%d" #: SourcesCitationsReport/SourcesCitationsReport.py:288 #, python-format msgid " %s" -msgstr "" +msgstr " %s" #: SourcesCitationsReport/SourcesCitationsReport.py:294 #, python-format msgid " - %s " -msgstr "" +msgstr " - %s " #: SourcesCitationsReport/SourcesCitationsReport.py:300 #, python-format msgid " Type: %s" -msgstr "" +msgstr " Tür: %s" #: SourcesCitationsReport/SourcesCitationsReport.py:302 #, python-format msgid " N-ID: %s" -msgstr "" +msgstr " N-Kimliği: %s" #: SourcesCitationsReport/SourcesCitationsReport.py:310 #, python-format msgid " %s" -msgstr "" +msgstr " %s" #: SourcesCitationsReport/SourcesCitationsReport.py:324 #: SourcesCitationsReport/SourcesCitationsReport.py:345 @@ -22741,193 +23228,196 @@ msgstr "" #: SourcesCitationsReport/SourcesCitationsReport.py:384 #, python-format msgid "%s" -msgstr "" +msgstr "%s" #: SourcesCitationsReport/SourcesCitationsReport.py:326 #: SourcesCitationsReport/SourcesCitationsReport.py:347 #, python-format msgid " ( %s )" -msgstr "" +msgstr " ( %s )" #: SourcesCitationsReport/SourcesCitationsReport.py:333 msgid " Spouses: " -msgstr "" +msgstr " Eş: " #: SourcesCitationsReport/SourcesCitationsReport.py:338 #, python-format msgid "and %s " -msgstr "" +msgstr "ve %s " #: SourcesCitationsReport/SourcesCitationsReport.py:349 #, python-format msgid " %s" -msgstr "" +msgstr " %s" #: SourcesCitationsReport/SourcesCitationsReport.py:413 msgid "Title of the Report" -msgstr "" +msgstr "Raporun Başlığı" #: SourcesCitationsReport/SourcesCitationsReport.py:414 msgid "Title string for the report." -msgstr "" +msgstr "Raporun başlık metni." #: SourcesCitationsReport/SourcesCitationsReport.py:417 msgid "Subtitle of the Report" -msgstr "" +msgstr "Raporun Alt Başlığı" #: SourcesCitationsReport/SourcesCitationsReport.py:418 msgid "Subtitle string for the report." -msgstr "" +msgstr "Raporun alt başlık metni." #: SourcesCitationsReport/SourcesCitationsReport.py:423 msgid "researcher name" -msgstr "" +msgstr "Araştırmacı adı" #: SourcesCitationsReport/SourcesCitationsReport.py:436 msgid "Select sources using a filter" -msgstr "" +msgstr "Kaynakları bir filtre kullanarak seçin" #: SourcesCitationsReport/SourcesCitationsReport.py:443 msgid "Show persons" -msgstr "" +msgstr "Kişileri göster" #: SourcesCitationsReport/SourcesCitationsReport.py:444 msgid "Whether to show events and persons mentioned in the note" -msgstr "" +msgstr "Notta bahsedilen etkinliklerin ve kişilerin gösterilip gösterilmeyeceği" #: SourcesCitationsReport/SourcesCitationsReport.py:495 msgid "The style used for the subtitle of the report." -msgstr "" +msgstr "Raporun alt başlığı için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:509 msgid "The style used for the footer of the report." -msgstr "" +msgstr "Raporun alt bilgisi için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:524 msgid "The style used for source title." -msgstr "" +msgstr "Kaynak başlığı için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:536 msgid "The style used for source subtitle." -msgstr "" +msgstr "Kaynak alt başlığı için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:548 msgid "The style used for Source details." -msgstr "" +msgstr "Kaynak ayrıntıları için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:563 msgid "The style used for citation title." -msgstr "" +msgstr "Alıntı başlığı için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:575 msgid "The style used for a column title." -msgstr "" +msgstr "Sütun başlığı için kullanılan stil." #: SourcesCitationsReport/SourcesCitationsReport.py:622 msgid "The style used for event and person details." -msgstr "" +msgstr "Etkinlik ve kişi detayları için kullanılan stil." #: Sqlite/ExportSql.py:1346 #, python-format msgid "Export Complete: %d second" msgid_plural "Export Complete: %d seconds" -msgstr[0] "" +msgstr[0] "Dışa Aktarma Tamamlandı: %d saniye" #: Sqlite/ImportSql.py:893 #, python-format msgid "Import Complete: %d second" msgid_plural "Import Complete: %d seconds" -msgstr[0] "" +msgstr[0] "Dışa Aktarma Tamamlandı: %d saniye" #: Sqlite/Sqlite.gpr.py:4 msgid "SQLite Import" -msgstr "" +msgstr "SQLite İçe Aktarma" #: Sqlite/Sqlite.gpr.py:5 Sqlite/Sqlite.gpr.py:20 msgid "SQLite is a common local database format" -msgstr "" +msgstr "SQLite yaygın bir yerel veritabanı biçimidir" #: Sqlite/Sqlite.gpr.py:19 msgid "SQLite Export" -msgstr "" +msgstr "SQLite Dışa Aktarma" #: SurnameMappingGramplet/SurnameMappingGramplet.grp.py:3 #: SurnameMappingGramplet/SurnameMappingGramplet.grp.py:11 msgid "Surname Mapping" -msgstr "" +msgstr "Soyadı Eşleme" #: SurnameMappingGramplet/SurnameMappingGramplet.grp.py:4 msgid "Gramplet for editing the grouping of surnames" -msgstr "" +msgstr "Soyadı gruplandırmalarını düzenlemek için Gramplet" #: SurnameMappingGramplet/SurnameMappingGramplet.py:67 msgid "Add Mapping" -msgstr "" +msgstr "Eşleme Ekle" #: SurnameMappingGramplet/SurnameMappingGramplet.py:68 #: SurnameMappingGramplet/SurnameMappingGramplet.py:158 msgid "Edit Mapping" -msgstr "" +msgstr "Eşlemeyi Düzenle" #: SurnameMappingGramplet/SurnameMappingGramplet.py:69 msgid "Remove Mapping" -msgstr "" +msgstr "Eşlemeyi Kaldır" #: SurnameMappingGramplet/SurnameMappingGramplet.py:78 msgid "Group Name" -msgstr "" +msgstr "Grup Adı" #: SurnameMappingGramplet/SurnameMappingGramplet.py:112 msgid "Group" -msgstr "" +msgstr "Grup" #: SurnameMappingGramplet/SurnameMappingGramplet.py:140 msgid "Create Mapping" -msgstr "" +msgstr "Eşleme Oluştur" #: SyncAssociations/syncAssociations.gpr.py:4 #: SyncAssociations/syncAssociations.py:130 #: SyncAssociations/syncAssociations.py:134 #: SyncAssociations/syncAssociations.py:138 msgid "Sync Associations" -msgstr "" +msgstr "İlişkilendirmeleri Eşitle" #: SyncAssociations/syncAssociations.gpr.py:6 msgid "" "Traverses the Person list for all Associations that are bi-directional and " "adds any which are missing to the Associated Person." msgstr "" +"İki yönlü olan tüm İlişkilendirmeler için Kişi listesini tarar ve eksik " +"olanları İlişkili Kişiye ekler." #: SyncAssociations/syncAssociations.py:124 #, python-format msgid "Add %s reciprocal association" -msgstr "" +msgstr "%s karşılıklı ilişkilendirmesini ekle" #: SyncAssociations/syncAssociations.py:131 msgid "{} Reciprocal associations created" -msgstr "" +msgstr "{} Karşılıklı ilişkilendirme oluşturuldu" #: SyncAssociations/syncAssociations.py:135 msgid "All reciprocal associations exist, none created" -msgstr "" +msgstr "Tüm karşılıklı ilişkilendirmeler mevcut, hiçbiri oluşturulmadı" #: SyncAssociations/syncAssociations.py:139 msgid "No existing associations, so no reciprocal ones needed" msgstr "" +"Mevcut ilişkilendirme yok, bu nedenle karşılıklı ilişkilendirme gerekmiyor" #: TMGimporter/importtmg.gpr.py:45 msgid "TMG Project Backup" -msgstr "" +msgstr "TMG Proje Yedeklemesi" #: TMGimporter/importtmg.gpr.py:46 TMGimporter/importtmg.gpr.py:67 #: TMGimporter/importtmg.gpr.py:82 TMGimporter/importtmg.gpr.py:97 msgid "Import TMG project files" -msgstr "" +msgstr "TMG proje dosyalarını içe aktar" #: TMGimporter/importtmg.gpr.py:66 TMGimporter/importtmg.gpr.py:81 #: TMGimporter/importtmg.gpr.py:96 msgid "TMG Unsupported" -msgstr "" +msgstr "TMG Desteklenmiyor" #: TMGimporter/importtmg.py:84 #, python-format @@ -22946,6 +23436,19 @@ msgid "" "Please refer to:\n" "%(gramps_wiki_import_pjc_direct_url)s" msgstr "" +"TMG İçe Aktarıcı Eklentisi, TMG dosyalarından\n" +"doğrudan içe aktarmayı desteklemez.\n" +"\n" +"TMG projesinin bir yedek kopyasını (*.sqz) kullanmanız gerekir\n" +"\n" +"TMG projenizin şu sürümle oluşturulduğundan emin olun:\n" +"TMG sürüm 5.x veya üzeri.\n" +"\n" +"Dosyanız:\n" +"*.PJC - TMG 5.0 ile TMG 9.05 için Proje Yapılandırma Dosyası\n" +"\n" +"Lütfen şuraya bakın:\n" +"%(gramps_wiki_import_pjc_direct_url)s" #: TMGimporter/importtmg.py:105 #, python-format @@ -22964,6 +23467,19 @@ msgid "" "Please refer to:\n" "%(gramps_wiki_import_pjc_direct_url)s" msgstr "" +"TMG İçe Aktarıcı Eklentisi, TMG dosyalarından\n" +"doğrudan içe aktarmayı desteklemez.\n" +"\n" +"TMG projesinin yedek bir kopyasını (*.sqz) kullanmanız gerekir\n" +"\n" +"TMG projenizin şu şekilde oluşturulduğundan emin olun:\n" +"TMG sürüm 5.x veya üzeri.\n" +"\n" +"Dosyanız:\n" +"*.TMG - TMG 2.0'dan TMG 4.0d'ye kadar Sürüm Kontrol Dosyası\n" +"\n" +"Lütfen şuraya bakın:\n" +"%(gramps_wiki_import_pjc_direct_url)s" #: TMGimporter/importtmg.py:126 #, python-format @@ -22982,14 +23498,27 @@ msgid "" "Please refer to:\n" "%(gramps_wiki_import_pjc_direct_url)s" msgstr "" +"TMG İçe Aktarıcı Eklentisi, TMG dosyalarından\n" +"doğrudan içe aktarmayı desteklemez.\n" +"\n" +"TMG projesinin yedek bir kopyasını (*.sqz) kullanmanız gerekir\n" +"\n" +"TMG projenizin şu şekilde oluşturulduğundan emin olun:\n" +"TMG sürüm 5.x veya üzeri.\n" +"\n" +"Dosyanız:\n" +"*.VER - TMG 1.2 ve öncesi için Sürüm Kontrol Dosyası\n" +"\n" +"Lütfen şuraya bakın:\n" +"%(gramps_wiki_import_pjc_direct_url)s" #: TMGimporter/libtmg.glade:15 msgid "TMG Importer" -msgstr "" +msgstr "TMG İçe Aktarıcı" #: TMGimporter/libtmg.glade:31 msgid "Unreleased Addon - Work In Progress" -msgstr "" +msgstr "Yayınlanmamış Eklenti - Geliştirme Aşamasında" #: TMGimporter/libtmg.glade:51 msgid "" @@ -22997,19 +23526,22 @@ msgid "" "\n" "Select the TMG Data Set you wish to import." msgstr "" +"Wholly Genes - Ana Soybilimci (TMG) İçe Aktarıcı\n" +"\n" +"İçe aktarmak istediğiniz TMG Veri Kümesini seçin." #: TMGimporter/libtmg.glade:88 msgid "Data Set Name" -msgstr "" +msgstr "Veri Kümesi Adı" #: TMGimporter/libtmg.glade:148 msgid "_Import TMG Data Set" -msgstr "" +msgstr "TMG Veri Kümesini _İçe Aktar" #: TMGimporter/libtmg.py:2319 TMGimporter/libtmg.py:2352 #: TMGimporter/libtmg.py:2434 TMGimporter/libtmg.py:2469 msgid "TMG import failed" -msgstr "" +msgstr "TMG içe aktarma başarısız oldu" #: TMGimporter/libtmg.py:2320 msgid "" @@ -23017,7 +23549,7 @@ msgid "" "\n" "Please create a new empty Family Tree before importing a TMG backup file." msgstr "" -"Mevcut Aile Ağacı boş değil.\n" +"Geçerli Aile Ağacı boş değil.\n" "\n" "Lütfen TMG yedekleme dosyasını içe aktarmadan önce yeni ve boş bir Aile " "Ağacı oluşturun." @@ -23030,10 +23562,14 @@ msgid "" "Please ensure you are using a backup created by TMG 9.02 or later " "(PjcVersion >= 11.0)." msgstr "" +"TMG yedekleme dosyası okunamadı: PJC sürüm bilgisi eksik veya okunamıyor.\n" +"\n" +"Lütfen TMG 9.02 veya daha yeni bir sürümle oluşturulmuş bir yedek " +"kullandığınızdan emin olun (PjcVersion >= 11.0)." #: TMGimporter/libtmg.py:2365 msgid "TMG version not supported" -msgstr "" +msgstr "TMG sürümü desteklenmiyor" #: TMGimporter/libtmg.py:2366 #, python-format @@ -23047,6 +23583,14 @@ msgid "" "See: https://gramps-project.org/wiki/index.php/" "Addon:TMGimporter#Before_Import_From_TMG_Backup_file" msgstr "" +"Bu yedekleme, TMG'nin eski bir sürümüyle (PjcVersion %(ver)s, TMG 9.01 veya " +"daha önceki sürümlere eşdeğer) oluşturulmuştur.\n" +"\n" +"Lütfen TMG projenizi 9.05 sürümüne yükseltin ve yeni bir yedekleme " +"oluşturun, ardından tekrar içe aktarın.\n" +"\n" +"Bakınız: https://gramps-project.org/wiki/index.php/" +"Eklenti:TMGimporter#TMG_Yedek_dosyasından_İçe_Aktarmadan_Önce" #: TMGimporter/libtmg.py:2435 msgid "" @@ -23054,6 +23598,9 @@ msgid "" "\n" "The backup may be corrupt or empty." msgstr "" +"Bu TMG yedekleme dosyasında hiçbir veri seti bulunamadı.\n" +"\n" +"Yedekleme dosyası bozuk veya boş olabilir." #: TMGimporter/libtmg.py:2470 #, python-format @@ -23065,812 +23612,896 @@ msgid "" "\n" "Ensure the file was created by TMG version 5.x or later." msgstr "" +"%(filename)s geçerli bir TMG yedekleme dosyası gibi görünmüyor.\n" +"\n" +"Dosya, bir proje yapılandırma dosyası (.PJC) içeren bir TMG yedekleme arşivi " +"(*.SQZ) olmalıdır.\n" +"\n" +"Dosyanın TMG sürüm 5.x veya sonraki bir sürüm tarafından oluşturulduğundan " +"emin olun." #: Themes/themes.gpr.py:29 msgid "Theme preferences" -msgstr "" +msgstr "Tema tercihleri" #: Themes/themes.gpr.py:31 msgid "" "An addition to Preferences for simple Theme and Font adjustment. Especially " "useful for Windows users." msgstr "" +"Tercihler bölümüne eklenen, tema ve yazı tipi ayarlarını kolaylaştıran bir " +"özellik. Özellikle Windows kullanıcıları için kullanışlıdır." #: Themes/themes.py:172 Themes/themes.py:190 msgid "Theme is hardcoded by GTK_THEME" -msgstr "" +msgstr "Tema, GTK_THEME tarafından kodlanmıştır" #: Themes/themes.py:180 msgid "Dark Variant" -msgstr "" +msgstr "Koyu Değişken" #: Themes/themes.py:198 msgid "Font" -msgstr "" +msgstr "Yazı Tipi" #: Themes/themes.py:214 msgid "Fixed Scrollbar (requires restart)" -msgstr "" +msgstr "Sabit Kaydırma Çubuğu (yeniden başlatma gerektirir)" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.gpr.py:7 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.gpr.py:16 msgid "This Day in Family History" -msgstr "" +msgstr "Aile Tarihinde Bugün" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.gpr.py:9 msgid "" "A configurable program that shows you the connected events from your family " "tree that match today's day and month." msgstr "" -"Aile ağacınızdaki bugünün günü ve ayına denk gelen bağlantılı olayları " +"Aile ağacınızdaki bugünün günü ve ayına denk gelen bağlantılı etkinlikleri " "gösteren, yapılandırılabilir bir program." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:105 msgid "Database is not open, can't check history right now." -msgstr "" +msgstr "Veritabanı açık değil, şu anda geçmişi kontrol edemiyorum." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:106 #, python-format msgid "On %(date)s in family history ...\n" -msgstr "" +msgstr "%(date)s tarihinde aile geçmişinde ...\n" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:107 msgid "... nothing happened! Check again tomorrow!" -msgstr "" +msgstr "... hiçbir şey olmadı! Yarın tekrar kontrol edin!" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:111 msgid "Report only living tree members" -msgstr "" +msgstr "Yalnızca yaşayan ağaç üyelerini rapor edin" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:112 msgid "Show these events" -msgstr "" +msgstr "Bu etkinlikleri göster" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:113 msgid "Sort by " -msgstr "" +msgstr "Şuna göre sırala " #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:114 msgid "Sort in ascending order?" -msgstr "" +msgstr "Artan düzende sıralansın mı?" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:127 msgid "February" -msgstr "" +msgstr "Şubat" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:127 msgid "January" -msgstr "" +msgstr "Ocak" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:127 msgid "March" -msgstr "" +msgstr "Mart" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:128 msgid "April" -msgstr "" +msgstr "Nisan" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:128 msgid "July" -msgstr "" +msgstr "Temmuz" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:128 msgid "June" -msgstr "" +msgstr "Haziran" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:128 msgid "May" -msgstr "" +msgstr "Mayıs" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:129 msgid "August" -msgstr "" +msgstr "Ağustos" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:129 msgid "October" -msgstr "" +msgstr "Ekim" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:129 msgid "September" -msgstr "" +msgstr "Eylül" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:130 msgid "December" -msgstr "" +msgstr "Aralık" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:130 msgid "November" -msgstr "" +msgstr "Kasım" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:247 msgid "Person Name" -msgstr "" +msgstr "Kişi Adı" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:249 msgid "Event Year" -msgstr "" +msgstr "Etkinlik Yılı" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:332 msgid "gregorian" -msgstr "" +msgstr "Gregoryen" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:387 msgid "Unknown father/partner" -msgstr "" +msgstr "Bilinmeyen baba/partner" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:402 msgid "Unknown mother/partner" -msgstr "" +msgstr "Bilinmeyen anne/partner" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:431 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:433 msgid " and " -msgstr "" +msgstr " ve " #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:435 msgid "unknown participant" -msgstr "" +msgstr "bilinmeyen katılımcı" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:442 msgid "unknown location" -msgstr "" +msgstr "bilinmeyen konum" #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:485 #, python-format msgid "%(male_name)s was adopted in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda evlat edinildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:487 #, python-format msgid "%(female_name)s was adopted in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda evlat edinildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:490 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:543 #, python-format msgid "%(male_name)s was christened in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumundda vaftiz edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:492 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:545 #, python-format msgid "%(female_name)s was christened in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda vaftiz edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:495 #, python-format msgid "%(male_name)s was married in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda evlendi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:497 #, python-format msgid "%(female_name)s was married in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda evlendi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:500 #, python-format msgid "%(male_name)s received an annulment in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda evliliğinin iptal " +"kararını aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:502 #, python-format msgid "%(female_name)s received an annulment in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda evliliğinin iptal " +"kararını aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:506 #, python-format msgid "%(male_name)s was baptized in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda vaftiz edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:508 #, python-format msgid "%(female_name)s was baptized in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda vaftiz edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:511 #, python-format msgid "%(male_name)s became a bar mitzvah in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda bar mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:513 #, python-format msgid "%(female_name)s became a bar mitzvah in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda bar mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:516 #, python-format msgid "%(male_name)s became a bat mitzvah in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda bat mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:518 #, python-format msgid "%(female_name)s became a bat mitzvah in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda bat mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:521 #, python-format msgid "%(male_name)s was born in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda doğdu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:523 #, python-format msgid "%(female_name)s was born in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda doğdu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:526 #, python-format msgid "%(male_name)s was blessed in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda kutsandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:528 #, python-format msgid "%(female_name)s was blessed in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda kutsandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:531 #, python-format msgid "%(male_name)s was buried in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda defnedildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:533 #, python-format msgid "%(female_name)s was buried in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda defnedildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:536 #, python-format msgid "%(male_name)s participated in a census in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda yapılan bir nüfus " +"sayımına katıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:539 #, python-format msgid "%(female_name)s participated in a census in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda yapılan bir nüfus " +"sayımına katıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:548 #, python-format msgid "%(male_name)s was confirmed in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda onaylandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:550 #, python-format msgid "%(female_name)s was confirmed in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda onaylandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:553 #, python-format msgid "%(male_name)s was cremated in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda yakıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:555 #, python-format msgid "%(female_name)s was cremated in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda yakıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:558 #, python-format msgid "%(male_name)s died in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda öldü." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:560 #, python-format msgid "%(female_name)s died in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda öldü." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:563 #, python-format msgid "%(male_name)s was awarded a degree in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir lisans derecesi " +"almıştır." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:565 #, python-format msgid "%(female_name)s was awarded a degree in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir lisans derecesi " +"almıştır." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:568 #, python-format msgid "%(male_name)s was granted a divorce in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir boşanma kararı aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:571 #, python-format msgid "%(female_name)s was granted a divorce in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir boşanma kararı " +"aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:575 #, python-format msgid "%(male_name)s filed for divorce in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda boşanma davası açtı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:577 #, python-format msgid "%(female_name)s filed for divorce in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda boşanma davası açtı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:580 #, python-format msgid "%(male_name)s was elected in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda seçildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:582 #, python-format msgid "%(female_name)s was elected in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda seçildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:585 #, python-format msgid "%(male_name)s emigrated in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda göç etti." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:587 #, python-format msgid "%(female_name)s emigrated in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda göç etti." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:590 #, python-format msgid "%(male_name)s became engaged in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda nişanlandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:592 #, python-format msgid "%(female_name)s became engaged in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda nişanlandı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:595 #, python-format msgid "%(male_name)s received first communion in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda ilk komünyonunu aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:598 #, python-format msgid "%(female_name)s received first communion in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda ilk komünyonunu aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:602 #, python-format msgid "%(male_name)s graduated in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda mezun oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:604 #, python-format msgid "%(female_name)s graduated in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda mezun oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:607 #, python-format msgid "%(male_name)s immigrated in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumuna göç etti." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:609 #, python-format msgid "%(female_name)s immigrated in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumuna göç etti." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:615 #, python-format msgid "%(male_name)s got married in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s %(year)s yılında %(place)s konumunda evlendi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:617 #, python-format msgid "%(female_name)s got married in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda evlendi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:620 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:631 #, python-format msgid "%(male_name)s joined as a family in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir aile olarak katıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:622 #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:633 #, python-format msgid "%(female_name)s joined as a family in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir aile olarak " +"katıldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:625 #, python-format msgid "%(male_name)s entered a civil union in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda medeni birlikteliğe " +"girdi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:627 #, python-format msgid "%(female_name)s entered a civil union in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda medeni birlikteliğe " +"girdi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:636 #, python-format msgid "%(male_name)s had a custom marriage in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda özel bir evlilik töreni " +"yaptı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:638 #, python-format msgid "%(female_name)s had a custom marriage in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda özel bir evlilik " +"töreni yaptı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:642 #, python-format msgid "%(male_name)s announced a marriage banns in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir evlilik ilanı " +"duyurdu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:645 #, python-format msgid "%(female_name)s announced a marriage banns in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir evlilik ilanı " +"duyurdu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:649 #, python-format msgid "%(male_name)s entered a marriage contract in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir evlilik sözleşmesi " +"yaptı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:652 #, python-format msgid "%(female_name)s entered a marriage contract in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir evlilik sözleşmesi " +"yaptı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:656 #, python-format msgid "%(male_name)s obtained a marriage license in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir evlilik ruhsatı aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:659 #, python-format msgid "%(female_name)s obtained a marriage license in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir evlilik ruhsatı " +"aldı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:663 #, python-format msgid "%(male_name)s obtained a marriage settlement in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda bir evlilik sözleşmesi " +"imzaladı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:666 #, python-format msgid "" "%(female_name)s obtained a marriage settlement in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bir evlilik sözleşmesi " +"imzaladı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:670 #, python-format msgid "%(male_name)s entered military service in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda askerlik hizmetine " +"başladı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:673 #, python-format msgid "%(female_name)s entered military service in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda askerlik hizmetine " +"başladı." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:677 #, python-format msgid "%(male_name)s became naturalized in %(year)s at %(place)s." msgstr "" +"%(male_name)s, %(year)s yılında %(place)s konumunda vatandaşlığa kabul " +"edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:679 #, python-format msgid "%(female_name)s became naturalized in %(year)s at %(place)s." msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda vatandaşlığa kabul " +"edildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:682 #, python-format msgid "%(male_name)s had a title bestowed in %(year)s at %(place)s." msgstr "" +"%(male_name)s adlı kişiye %(year)s yılında %(place)s konumunda bir unvan " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:684 #, python-format msgid "%(female_name)s had a title bestowed in %(year)s at %(place)s." msgstr "" +"%(female_name)s adlı kişiye %(year)s yılında %(place)s konumunda bir unvan " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:687 #, python-format msgid "%(male_name)s was ordained in %(year)s at %(place)s." msgstr "" +"%(male_name)s adlı kişiye %(year)s yılında %(place)s konumunda rütbesi " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:689 #, python-format msgid "%(female_name)s was ordained in %(year)s at %(place)s." msgstr "" +"%(female_name)s adlı kişiye %(year)s yılında %(place)s konumunda rütbesi " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:692 #, python-format msgid "%(male_name)s was granted probate in %(year)s at %(place)s." msgstr "" +"%(male_name)s için %(year)s yılında %(place)s konumunda vasiyetname onayı " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:694 #, python-format msgid "%(female_name)s was granted probate in %(year)s at %(place)s." msgstr "" +"%(female_name)s için %(year)s yılında %(place)s konumunda vasiyetname onayı " +"verildi." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:697 #, python-format msgid "%(male_name)s retired in %(year)s at %(place)s." -msgstr "" +msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda emekli oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:699 #, python-format msgid "%(female_name)s retired in %(year)s at %(place)s." -msgstr "" +msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda emekli oldu." #: ThumbnailGenerator/ThumbnailGenerator.gpr.py:31 #: ThumbnailGenerator/ThumbnailGenerator.py:53 msgid "Thumbnail Generator" -msgstr "" +msgstr "Küçük Resim Oluşturucu" #: ThumbnailGenerator/ThumbnailGenerator.gpr.py:32 msgid "Generates thumbnails for media files" -msgstr "" +msgstr "Medya dosyaları için küçük resimler oluşturur" #: ThumbnailGenerator/ThumbnailGenerator.py:56 msgid "Generating media thumbnails" -msgstr "" +msgstr "Medya küçük resimleri oluşturma" #: ThumbnailGenerator/ThumbnailGenerator.py:67 msgid "Generating thumbnails for person references" -msgstr "" +msgstr "Kişi referansları için küçük resimler oluşturma" #: ThumbnailGenerator/ThumbnailGenerator.py:76 msgid "Generating thumbnails for family references" -msgstr "" +msgstr "Aile referansları için küçük resimler oluşturma" #: ThumbnailGenerator/ThumbnailGenerator.py:85 msgid "Generating thumbnails for event references" -msgstr "" +msgstr "Etkinlik referansları için küçük resimler oluşturma" #: ThumbnailGenerator/ThumbnailGenerator.py:94 msgid "Generating thumbnails for place references" -msgstr "" +msgstr "Yer referansları için küçük resimler oluşturma" #: ThumbnailGenerator/ThumbnailGenerator.py:103 msgid "Generating thumbnails for source references" -msgstr "" +msgstr "Kaynak referansları için küçük resimler oluşturma" #: TimePedigreeHTML/TimePedigreeHtml.gpr.py:47 msgid "Timeline Pedigree Report" -msgstr "" +msgstr "Zaman Çizelgesi Soy Ağacı Raporu" #: TimePedigreeHTML/TimePedigreeHtml.gpr.py:49 msgid "" "Creates an HTML webpage that uses JavaScript to show a pedigree block " "diagram arranged vertically by birth date" msgstr "" +"Doğum tarihine göre dikey olarak düzenlenmiş bir soyağacı blok diyagramı " +"göstermek için JavaScript kullanan bir HTML web sayfası oluşturur" #: TimePedigreeHTML/TimePedigreeHtml.py:167 msgid "Failed writing " -msgstr "" +msgstr "Yazma başarısız oldu " #: TimePedigreeHTML/TimePedigreeHtml.py:860 msgid "Year of Birth of Center Person" -msgstr "" +msgstr "Merkez Kişinin Doğum Yılı" #: TimePedigreeHTML/TimePedigreeHtml.py:863 msgid "The year of birth of the center person. Estimate a year if unknown" -msgstr "" +msgstr "Merkez kişinin doğum yılı. Bilinmiyorsa tahmini bir yıl girin" #: TimePedigreeHTML/TimePedigreeHtml.py:868 msgid "Destination directory" -msgstr "" +msgstr "Hedef dizin" #: TimePedigreeHTML/TimePedigreeHtml.py:871 msgid "Path for generated files" -msgstr "" +msgstr "Oluşturulan dosyaların yolu" #: TimePedigreeHTML/TimePedigreeHtml.py:879 msgid "Default Age" -msgstr "" +msgstr "Varsayılan Yaş" #: TimePedigreeHTML/TimePedigreeHtml.py:880 msgid "Default age of parent when child is born with unknown year of birth" -msgstr "" +msgstr "Çocuğun doğum yılı bilinmiyorsa ebeveynin varsayılan yaşı" #: TimePedigreeHTML/TimePedigreeHtml.py:889 msgid "What to show" -msgstr "" +msgstr "Ne gösterilsin" #: TimePedigreeHTML/TimePedigreeHtml.py:891 msgid "Show GrampsID" -msgstr "" +msgstr "Gramps Kimliğini Göster" #: TimePedigreeHTML/TimePedigreeHtml.py:892 msgid "Show GrampsID on top right of the box" -msgstr "" +msgstr "Kutunun sağ üst köşesinde Gramps Kimliğini göster" #: TimePedigreeHTML/TimePedigreeHtml.py:895 msgid "Optimize" -msgstr "" +msgstr "Optimize Et" #: TimePedigreeHTML/TimePedigreeHtml.py:896 msgid "Use as little space in horizontal direction as possible" -msgstr "" +msgstr "Yatay yönde mümkün olduğunca az yer kullan" #: TimePedigreeHTML/TimePedigreeHtml.py:904 msgid "Male Box Color" -msgstr "" +msgstr "Erkek Kutu Rengi" #: TimePedigreeHTML/TimePedigreeHtml.py:905 msgid "Box background color for male person" -msgstr "" +msgstr "Erkek kişi kutusunun arka plan rengi" #: TimePedigreeHTML/TimePedigreeHtml.py:908 msgid "Female Box Color" -msgstr "" +msgstr "Kadın Kutu Rengi" #: TimePedigreeHTML/TimePedigreeHtml.py:909 msgid "Box background color for female person" -msgstr "" +msgstr "Kadın kişi kutusunun arka plan rengi" #: TimePedigreeHTML/TimePedigreeHtml.py:915 msgid "Number of pixel per year" -msgstr "" +msgstr "Yıl başına piksel sayısı" #: TimePedigreeHTML/TimePedigreeHtml.py:916 msgid "How many pixel height is one year?" -msgstr "" +msgstr "Bir yılın yüksekliği kaç piksel olsun?" #: TimePedigreeHTML/TimePedigreeHtml.py:921 msgid "Offset X Coordinate" -msgstr "" +msgstr "X Koordinatı Ofseti" #: TimePedigreeHTML/TimePedigreeHtml.py:923 msgid "How many pixel are unused besides the most left and most right boxes?" msgstr "" +"En soldaki ve en sağdaki kutuların dışında kaç piksel kullanılmadan " +"bırakılacak?" #: TimePedigreeHTML/TimePedigreeHtml.py:928 msgid "Offset Y Coordinate" -msgstr "" +msgstr "Y Koordinatı Ofseti" #: TimePedigreeHTML/TimePedigreeHtml.py:929 msgid "How many pixel are unused above first box?" -msgstr "" +msgstr "İlk kutunun üstünde kaç piksel kullanılmadan bırakılacak?" #: TimePedigreeHTML/TimePedigreeHtml.py:932 msgid "Width of a box in pixel" -msgstr "" +msgstr "Bir kutunun piksel cinsinden genişliği" #: TimePedigreeHTML/TimePedigreeHtml.py:933 msgid "Width of the box of a person in pixel" -msgstr "" +msgstr "Bir kişi kutusunun piksel cinsinden genişliği" #: TimePedigreeHTML/TimePedigreeHtml.py:936 msgid "Horizontal space between boxes in pixel" -msgstr "" +msgstr "Kutular arasındaki yatay mesafe piksel cinsinden" #: TimePedigreeHTML/TimePedigreeHtml.py:939 msgid "Minimum horizontal space between 2 boxes in pixel" -msgstr "" +msgstr "İki kutu arasındaki minimum yatay mesafe piksel cinsinden" #: TimePedigreeHTML/TimePedigreeHtml.py:945 msgid "Length of vertical part of a line in pixel" -msgstr "" +msgstr "Bir çizginin dikey kısmının piksel cinsinden uzunluğu" #: TimePedigreeHTML/TimePedigreeHtml.py:947 msgid "" "Parent and child boxes are connected with lines. This is the length of the " "vertical part of the lines in pixel" msgstr "" +"Ebeveyn ve çocuk kutuları çizgilerle birbirine bağlanmıştır. Bu, çizgilerin " +"dikey kısmının piksel cinsinden uzunluğudur" #: TimePedigreeHTML/TimePedigreeHtml.py:954 #: TimePedigreeHTML/TimePedigreeHtml.py:956 msgid "Number of pixel between left border and scale" -msgstr "" +msgstr "Sol kenarlık ile ölçek arasındaki piksel sayısı" #: TimelinePedigreeView/TimelinePedigreeView.gpr.py:33 #: TimelinePedigreeView/TimelinePedigreeView.gpr.py:46 msgid "Timeline Pedigree" -msgstr "" +msgstr "Zaman Çizelgesi Soy Ağacı" #: TimelinePedigreeView/TimelinePedigreeView.gpr.py:36 msgid "" "The view shows a timeline pedigree with ancestors and descendants of the " "selected person" msgstr "" +"Bu görünüm, seçilen kişinin atalarını ve torunlarını içeren bir zaman " +"çizelgesi soy ağacını gösterir" #: TimelinePedigreeView/TimelinePedigreeView.py:339 msgid "Timeline pedigree" -msgstr "" +msgstr "Zaman çizelgesi soy ağacı" #: TimelinePedigreeView/TimelinePedigreeView.py:1437 msgid "Order by timeline" -msgstr "" +msgstr "Zaman çizelgesine göre sırala" #: TimelinePedigreeView/TimelinePedigreeView.py:1439 msgid "Show lifespan" -msgstr "" +msgstr "Yaşam süresini göster" #: TimelinePedigreeView/TimelinePedigreeView.py:1470 #: TimelinePedigreeView/TimelinePedigreeView.py:1486 #, python-format msgid "%d generation" msgid_plural "%d generations" -msgstr[0] "" +msgstr[0] "%d nesil" #: TimelinePedigreeView/TimelinePedigreeView.py:1504 msgid "About Timeline Pedigree View" -msgstr "" +msgstr "Zaman Çizelgesi Soy Ağacı Görünümü Hakkında" #: TimelineQuickview/TimelineQuickview.gpr.py:11 msgid "Display a person's events on a timeline" -msgstr "" +msgstr "Bir kişinin etkinliklerini zaman çizelgesinde görüntüle" #: TimelineQuickview/TimelineQuickview.py:48 msgid "Inlaw Parents" -msgstr "" +msgstr "Evlilik Yoluyla Ebeveynler" #: TimelineQuickview/TimelineQuickview.py:51 msgid "Grandparents" -msgstr "" +msgstr "Büyükanneler ve Büyükbabalar" #: TimelineQuickview/TimelineQuickview.py:53 msgid "Inlaw Grandparents" -msgstr "" +msgstr "Evlilik Yoluyla Büyükanneler ve Büyükbabalar" #: TimelineQuickview/TimelineQuickview.py:56 msgid "Great grandparents" -msgstr "" +msgstr "Büyük Büyükanneler ve Büyükbabalar" #: TimelineQuickview/TimelineQuickview.py:58 msgid "Inlaw Great grandparents" -msgstr "" +msgstr "Evlilik Yoluyla Büyük Büyükanneler ve Büyükbabalar" #: TimelineQuickview/TimelineQuickview.py:61 msgid "Great, " -msgstr "" +msgstr "Büyük, " #: TimelineQuickview/TimelineQuickview.py:61 #: TimelineQuickview/TimelineQuickview.py:63 msgid "great grandparents" -msgstr "" +msgstr "büyükanneler ve büyükbabalar" #: TimelineQuickview/TimelineQuickview.py:61 #: TimelineQuickview/TimelineQuickview.py:63 msgid "great, " -msgstr "" +msgstr "büyük, " #: TimelineQuickview/TimelineQuickview.py:63 msgid "Inlaw Great, " -msgstr "" +msgstr "Evlilik Yoluyla Büyük, " #: TimelineQuickview/TimelineQuickview.py:102 msgid "Partner's spouse" -msgstr "" +msgstr "Partnerin eşi" #: TimelineQuickview/TimelineQuickview.py:156 #, python-format msgid "Timeline for %s" -msgstr "" +msgstr "%s için zaman çizelgesi" #: TimelineQuickview/TimelineQuickview.py:163 msgid "People involved" -msgstr "" +msgstr "İlgili kişiler" #: ToDoReport/TodoReport.gpr.py:25 msgid "Todo Report" -msgstr "" +msgstr "Yapılacaklar Raporu" #: ToDoReport/TodoReport.gpr.py:27 msgid "" "Produces a list of all the notes with a given tag along with the records " "that it references, the Person, Family, Event, etc." msgstr "" +"Belirli bir etikete sahip tüm notların listesini, referans verdiği " +"kayıtlarla (Kişi, Aile, Olay vb.) birlikte oluşturur." #: ToDoReport/TodoReport.py:107 ToDoReport/TodoReport.py:904 msgid "TR-Title" -msgstr "" +msgstr "TR-Başlık" #: ToDoReport/TodoReport.py:111 #, python-format msgid "Report on Notes Tagged '%s'" -msgstr "" +msgstr "'%s' Etiketli Notlar Raporu" #: ToDoReport/TodoReport.py:237 ToDoReport/TodoReport.py:914 msgid "TR-Heading" -msgstr "" +msgstr "TR-Başlık" #: ToDoReport/TodoReport.py:243 msgid "NoteTable" -msgstr "" +msgstr "Not Tablosu" #: ToDoReport/TodoReport.py:243 ToDoReport/TodoReport.py:962 msgid "TR-Table" -msgstr "" +msgstr "TR-Tablo" #: ToDoReport/TodoReport.py:247 ToDoReport/TodoReport.py:253 #: ToDoReport/TodoReport.py:267 ToDoReport/TodoReport.py:273 @@ -23895,12 +24526,12 @@ msgstr "" #: ToDoReport/TodoReport.py:678 ToDoReport/TodoReport.py:683 #: ToDoReport/TodoReport.py:689 ToDoReport/TodoReport.py:947 msgid "TR-TableCell" -msgstr "" +msgstr "TR-Tablo Hücresi" #: ToDoReport/TodoReport.py:248 ToDoReport/TodoReport.py:254 #: ToDoReport/TodoReport.py:935 msgid "TR-Normal-Bold" -msgstr "" +msgstr "TR-Normal-Kalın" #: ToDoReport/TodoReport.py:268 ToDoReport/TodoReport.py:285 #: ToDoReport/TodoReport.py:303 ToDoReport/TodoReport.py:311 @@ -23924,47 +24555,47 @@ msgstr "" #: ToDoReport/TodoReport.py:684 ToDoReport/TodoReport.py:690 #: ToDoReport/TodoReport.py:924 msgid "TR-Normal" -msgstr "" +msgstr "TR-Normal" #: ToDoReport/TodoReport.py:275 ToDoReport/TodoReport.py:942 msgid "TR-Note" -msgstr "" +msgstr "TR-Not" #: ToDoReport/TodoReport.py:284 ToDoReport/TodoReport.py:952 msgid "TR-BorderCell" -msgstr "" +msgstr "TR-Kenar Hücresi" #: ToDoReport/TodoReport.py:405 msgid "date: " -msgstr "" +msgstr "tarih: " #: ToDoReport/TodoReport.py:417 msgid "place: " -msgstr "" +msgstr "yer: " #: ToDoReport/TodoReport.py:877 msgid "Note Type" -msgstr "" +msgstr "Not Türü" #: ToDoReport/TodoReport.py:885 msgid "Group by reference type" -msgstr "" +msgstr "Referans türüne göre gruplandır" #: ToDoReport/TodoReport.py:886 msgid "Group notes by Family, Person, Place, etc." -msgstr "" +msgstr "Notları Aile, Kişi, Yer vb. türlere göre grupla." #: ToDoReport/TodoReport.py:946 msgid "The basic style used for the table cell display." -msgstr "" +msgstr "Tablo hücresi görüntüleme için kullanılan temel stil." #: ToDoReport/TodoReport.py:951 msgid "The basic style used for the table border cell display." -msgstr "" +msgstr "Tablo kenarlık hücresi görüntüleme için kullanılan temel stil." #: ToDoReport/TodoReport.py:961 msgid "The basic style used for the table display." -msgstr "" +msgstr "Tablo görüntüleme için kullanılan temel stil." #: Topola/Topola.gpr.py:4 msgid "Interactive Family Tree" @@ -23972,43 +24603,43 @@ msgstr "İnteraktif Aile Ağacı" #: Topola/Topola.gpr.py:5 msgid "Opens an interactive tree in the browser" -msgstr "" +msgstr "Tarayıcıda etkileşimli bir ağaç açar" #: TypeCleanup/type_cleanup.gpr.py:30 msgid "Type Cleanup" -msgstr "" +msgstr "Tür Temizleme" #: TypeCleanup/type_cleanup.gpr.py:31 msgid "Clean up (remove) custom types" -msgstr "" +msgstr "Özel türleri temizle (kaldır)" #: TypeCleanup/type_cleanup.py:101 msgid "Event Roles" -msgstr "" +msgstr "Etkinlik Rolleri" #: TypeCleanup/type_cleanup.py:102 msgid "Event Types" -msgstr "" +msgstr "Etkinlik Türleri" #: TypeCleanup/type_cleanup.py:103 msgid "Family Relation Types" -msgstr "" +msgstr "Aile İlişkisi Türleri" #: TypeCleanup/type_cleanup.py:104 msgid "Child reference Types" -msgstr "" +msgstr "Çocuk Referans Türleri" #: TypeCleanup/type_cleanup.py:105 msgid "Name Origin Types" -msgstr "" +msgstr "Ad Kökeni Türleri" #: TypeCleanup/type_cleanup.py:106 msgid "Name Types" -msgstr "" +msgstr "Ad Türleri" #: TypeCleanup/type_cleanup.py:107 msgid "Note Types" -msgstr "" +msgstr "Not Türleri" #: TypeCleanup/type_cleanup.py:108 msgid "Place Types" @@ -24016,56 +24647,60 @@ msgstr "Yer Türleri" #: TypeCleanup/type_cleanup.py:109 msgid "Repository Types" -msgstr "" +msgstr "Depo Türleri" #: TypeCleanup/type_cleanup.py:111 msgid "Source Media Types" -msgstr "" +msgstr "Kaynak Medya Türleri" #: TypeCleanup/type_cleanup.py:112 msgid "URL Types" -msgstr "" +msgstr "URL Türleri" #: TypeCleanup/type_cleanup.py:124 msgid "Types Cleanup Tool" -msgstr "" +msgstr "Tür Temizleme Aracı" #: TypeCleanup/type_cleanup.py:166 msgid "" "Remove the selected type from the db.\n" "This does not change any referenced objects." msgstr "" +"Seçilen türü veritabanından kaldırır.\n" +"Bu, referans verilen nesnelerde herhangi bir değişikliğe neden olmaz." #: TypeCleanup/type_cleanup.py:174 msgid "" "Select a new name for the current type, prior to renaming the type in the " "referenced objects." msgstr "" +"Referans verilen nesnelerdeki türü yeniden adlandırmadan önce, geçerli tür " +"için yeni bir ad seçin." #: TypeCleanup/type_cleanup.py:182 msgid "Rename" -msgstr "" +msgstr "Yeniden adlandır" #: TypeCleanup/type_cleanup.py:185 msgid "Rename the selected type in all referenced objects." -msgstr "" +msgstr "Seçilen türü tüm referans verilen nesnelerde yeniden adlandırın." #: TypeCleanup/type_cleanup.py:189 msgid "Close the Type Cleanup Tool" -msgstr "" +msgstr "Tür Temizleme Aracını kapat" #: TypeCleanup/type_cleanup.py:195 msgid "Types" -msgstr "" +msgstr "Türler" #: TypeCleanup/type_cleanup.py:242 msgid "Type Cleanup Tool" -msgstr "" +msgstr "Tür Temizleme Aracı" #: TypeCleanup/type_cleanup.py:387 #, python-format msgid "Changing type from %(old_type)s to %(new_type)s." -msgstr "" +msgstr "%(old_type)s türü %(new_type)s olarak değiştirme." #: TypeCleanup/type_cleanup.py:432 msgid "" @@ -24076,104 +24711,112 @@ msgid "" "However, it will not remove it from the referenced items in the database.\n" "If you want to change it in referenced items, use 'Rename'." msgstr "" +"Bu özel türü kaldırmak, onu tür seçimi açılır listelerinden kaldıracaktır\n" +"ve bu aracın sonraki çalıştırmalarında artık kullanılamayacaktır.\n" +"\n" +"Ancak, veritabanındaki referans verilen öğelerden kaldırılmayacaktır.\n" +"Referans verilen öğelerde de değiştirmek istiyorsanız, 'Yeniden Adlandır' " +"seçeneğini kullanın." #: UAWebConnectPack/UAWebPack.gpr.py:11 msgid "UA Web Connect Pack" -msgstr "" +msgstr "UA Web Bağlantı Paketi" #: UAWebConnectPack/UAWebPack.gpr.py:12 msgid "Collection of Web sites for the UA (requires libwebconnect)" -msgstr "" +msgstr "UA için web siteleri koleksiyonu (libwebconnect gerektirir)" #: UAWebConnectPack/UAWebPack.py:39 msgid "Forum: ukrgenealogy.com.ua" -msgstr "" +msgstr "Forum: ukrgenealogy.com.ua" #: UAWebConnectPack/UAWebPack.py:40 msgid "Forum: genoua.name" -msgstr "" +msgstr "Forum: genoua.name" #: UAWebConnectPack/UAWebPack.py:43 msgid "Encyclopedia: esu.com.ua" -msgstr "" +msgstr "Ansiklopedi: esu.com.ua" #: UAWebConnectPack/UAWebPack.py:44 msgid "Wikipedia: uk.wikipedia.org" -msgstr "" +msgstr "Wikipedia: uk.wikipedia.org" #: UAWebConnectPack/UAWebPack.py:47 msgid "Repressed DB: reabit.org.ua" -msgstr "" +msgstr "Bastırılmış Veritabanı: reabit.org.ua" #: UAWebConnectPack/UAWebPack.py:50 msgid "Genealogy DB: familysearch.org" -msgstr "" +msgstr "Şecere Veritabanı: familysearch.org" #: UAWebConnectPack/UAWebPack.py:51 msgid "Genealogy DB: myheritage.com.ua" -msgstr "" +msgstr "Şecere Veritabanı: myheritage.com.ua" #: UAWebConnectPack/UAWebPack.py:53 msgid "Genealogy DB: pra.in.ua" -msgstr "" +msgstr "Şecere Veritabanı: pra.in.ua" #: UAWebConnectPack/UAWebPack.py:56 msgid "Map: Ridni.org" -msgstr "" +msgstr "Harita: Ridni.org" #: UAWebConnectPack/UAWebPack.py:59 msgid "Search: google.com.ua" -msgstr "" +msgstr "Arama: google.com.ua" #: UAWebConnectPack/UAWebPack.py:60 msgid "Search: roots.in.ua" -msgstr "" +msgstr "Arama: roots.in.ua" #: UKWebConnectPack/UKWebPack.gpr.py:11 msgid "UK Web Connect Pack" -msgstr "" +msgstr "BK Web Bağlantı Paketi" #: UKWebConnectPack/UKWebPack.gpr.py:12 msgid "Collection of Web sites for the UK (requires libwebconnect)" -msgstr "" +msgstr "BK için web siteleri koleksiyonu (libwebconnect gerektirir)" #: UKWebConnectPack/UKWebPack.py:33 msgid "UK Google" -msgstr "" +msgstr "BK Google" #: UKWebConnectPack/UKWebPack.py:34 msgid "British, UK, and Ireland" -msgstr "" +msgstr "Britanya, BK ve İrlanda" #: UKWebConnectPack/UKWebPack.py:37 msgid "National Archives" -msgstr "" +msgstr "Ulusal Arşivler" #: UKWebConnectPack/UKWebPack.py:38 USWebConnectPack/USWebPack.py:41 msgid "Hathi Trust Digital Library" -msgstr "" +msgstr "Hathi Trust Dijital Kütüphanesi" #: USWebConnectPack/USWebPack.gpr.py:11 msgid "US Web Connect Pack" -msgstr "" +msgstr "ABD Web Bağlantı Paketi" #: USWebConnectPack/USWebPack.gpr.py:12 msgid "Collection of Web sites for the US (requires libwebconnect)" -msgstr "" +msgstr "ABD için web siteleri koleksiyonu (libwebconnect gerektirir)" #: USWebConnectPack/USWebPack.py:35 msgid "US Google" -msgstr "" +msgstr "ABD Google" #: WebSearch/WebSearch.gpr.py:34 WebSearch/WebSearch.gpr.py:47 msgid "WebSearch" -msgstr "" +msgstr "Web Arama" #: WebSearch/WebSearch.gpr.py:36 msgid "" "Customized queries for online services based on the active Person, Place, " "Family, or Source record" msgstr "" +"Etkin Kişi, Yer, Aile veya Kaynak kaydına dayalı çevrimiçi hizmetler için " +"özelleştirilmiş sorgular" #: WebSearch/WebSearch.py:477 msgid "Save Coordinates to the Place" @@ -24181,91 +24824,95 @@ msgstr "Yer koordinatlarını kaydet" #: WebSearch/WebSearch.py:618 msgid "AI provider is disabled" -msgstr "" +msgstr "Yapay zekâ sağlayıcısı devre dışı bırakıldı" #: WebSearch/WebSearch.py:622 msgid "No AI API key provided" -msgstr "" +msgstr "Yapay zekâ API anahtarı sağlanmadı" #: WebSearch/WebSearch.py:628 msgid "AI-generated historical place data is currently disabled" msgstr "" +"Yapay zekâ tarafından oluşturulan tarihsel yer verileri şu anda devre dışı " +"bırakıldı" #: WebSearch/WebSearch.py:638 msgid "AI provider is unknown. Please check your AI provider settings." msgstr "" +"Yapay zekâ sağlayıcısı bilinmiyor. Lütfen yapay zekâ sağlayıcı ayarlarınızı " +"kontrol edin." #: WebSearch/WebSearch.py:666 msgid "⏳ Generating historical place data, please wait..." -msgstr "" +msgstr "⏳ Tarihsel yer verileri oluşturuluyor, lütfen bekleyin..." #: WebSearch/WebSearch.py:1630 msgid "Keys" -msgstr "" +msgstr "Anahtarlar" #: WebSearch/WebSearch.py:1632 msgid "Website URL" -msgstr "" +msgstr "Web sitesi URL adresi" #: WebSearch/WebSearch.py:1635 msgid "Add link to note" -msgstr "" +msgstr "Bağlantıyı nota ekle" #: WebSearch/WebSearch.py:1637 msgid "Add link to attribute" -msgstr "" +msgstr "Bağlantıyı özniteliğe ekle" #: WebSearch/WebSearch.py:1639 msgid "Show QR-code" -msgstr "" +msgstr "QR kodunu göster" #: WebSearch/WebSearch.py:1641 msgid "Copy link to clipboard" -msgstr "" +msgstr "Bağlantıyı panoya kopyala" #: WebSearch/WebSearch.py:1644 msgid "Hide link for selected item" -msgstr "" +msgstr "Seçilen öğe için bağlantıyı gizle" #: WebSearch/WebSearch.py:1647 msgid "Hide link for all items" -msgstr "" +msgstr "Tüm öğeler için bağlantıyı gizle" #: WebSearch/WebSearch.py:1651 msgid "Edit Attribute with the Link" -msgstr "" +msgstr "Bağlantılı Özniteliği Düzenle" #: WebSearch/WebSearch.py:1655 msgid "Edit Note with the Link" -msgstr "" +msgstr "Bağlantılı Notu Düzenle" #: WebSearch/WebSearch.py:1658 msgid "Edit Internet link" -msgstr "" +msgstr "İnternet bağlantısını düzenle" #: WebSearch/WebSearch.py:1661 msgid "🔍 AI Suggestions" -msgstr "" +msgstr "🔍 Yapay Zekâ Önerileri" #: WebSearch/WebSearch.py:1915 msgid "Attribute no longer matches this WebSearch row" -msgstr "" +msgstr "Öznitelik artık bu Web Arama satırıyla eşleşmiyor" #: WebSearch/WebSearch.py:1942 msgid "Attribute no longer exists. The extra icon is removed" -msgstr "" +msgstr "Öznitelik artık mevcut değil. Ek simge kaldırıldı" #: WebSearch/WebSearch.py:1983 msgid "Note no longer exists" -msgstr "" +msgstr "Not artık mevcut değil" #: WebSearch/WebSearch.py:2005 msgid "Note no longer exists. The extra icon is removed" -msgstr "" +msgstr "Not artık mevcut değil. Ek simge kaldırıldı" #: WebSearch/WebSearch.py:2057 msgid "Internet link no longer matches this WebSearch row" -msgstr "" +msgstr "İnternet bağlantısı artık bu Web Arama satırıyla eşleşmiyor" #: WebSearch/WebSearch.py:2148 #, python-brace-format @@ -24278,27 +24925,34 @@ msgid "" "You can use this link to revisit the source and verify the information " "related to this entity." msgstr "" +"📌 Bu '{title}' web bağlantısı, gelecekte başvurmak üzere WebSearch " +"gramplet'i (sürüm {version}) tarafından arşivlendi:\n" +"\n" +"🔗 {url}\n" +"\n" +"Bu bağlantıyı, kaynağı yeniden ziyaret etmek ve bu varlıkla ilgili bilgileri " +"doğrulamak için kullanabilirsiniz." #: WebSearch/WebSearch.py:2229 #, python-format msgid "Note #%(id)s has been successfully added" -msgstr "" +msgstr "Not #%(id)s başarıyla eklendi" #: WebSearch/WebSearch.py:2232 msgid "Error creating note" -msgstr "" +msgstr "Not oluşturma hatası" #: WebSearch/WebSearch.py:2252 msgid "URL is copied to the Clipboard" -msgstr "" +msgstr "URL panoya kopyalandı" #: WebSearch/WebSearch.py:2391 msgid "WebSearch Link" -msgstr "" +msgstr "Web Arama Bağlantısı" #: WebSearch/WebSearch.py:2428 msgid "Attribute has been successfully added" -msgstr "" +msgstr "Öznitelik başarıyla eklendi" #: WebSearch/WebSearch.py:2460 #, python-brace-format @@ -24318,70 +24972,70 @@ msgstr "Boş: {keys}" #: WebSearch/WebSearch.py:2469 #, python-brace-format msgid "Comment: {comment}" -msgstr "" +msgstr "Yorum: {comment}" #: WebSearch/activity_row_generator.py:120 #, python-format msgid "Visited: %s" -msgstr "" +msgstr "Ziyaret edildi: %s" #: WebSearch/activity_row_generator.py:124 #: WebSearch/activity_row_generator.py:139 #, python-format msgid "Link: %s" -msgstr "" +msgstr "Bağlantı: %s" #: WebSearch/activity_row_generator.py:126 #, python-format msgid "Attribute: %s" -msgstr "" +msgstr "Öznitelik: %s" #: WebSearch/activity_row_generator.py:128 #, python-format msgid "Value: %s" -msgstr "" +msgstr "Değer: %s" #: WebSearch/activity_row_generator.py:133 #, python-format msgid "Loaded from file: %s" -msgstr "" +msgstr "Dosyadan yüklendi: %s" #: WebSearch/activity_row_generator.py:136 #, python-format msgid "Domain: %s" -msgstr "" +msgstr "Alan adı: %s" #: WebSearch/activity_row_generator.py:141 #, python-format msgid "Object: %s" -msgstr "" +msgstr "Nesne: %s" #: WebSearch/activity_row_generator.py:145 #, python-format msgid "Pattern: %s" -msgstr "" +msgstr "Desen: %s" #: WebSearch/activity_row_generator.py:148 #, python-format msgid "Object Gramps ID: %s" -msgstr "" +msgstr "Nesne Gramps Kimliği: %s" #: WebSearch/activity_row_generator.py:152 #, python-format msgid "%s: %s → %s: %s" -msgstr "" +msgstr "%s: %s → %s: %s" #: WebSearch/activity_row_generator.py:162 msgid "Link visited" -msgstr "" +msgstr "Bağlantı ziyaret edildi" #: WebSearch/activity_row_generator.py:163 msgid "Link saved to Note" -msgstr "" +msgstr "Bağlantı Nota kaydedildi" #: WebSearch/activity_row_generator.py:164 msgid "Link saved to Attribute" -msgstr "" +msgstr "Bağlantı Özniteliğe kaydedildi" #: WebSearch/activity_row_generator.py:165 msgid "Place history loaded" @@ -24389,19 +25043,19 @@ msgstr "Yer geçmişi yüklendi" #: WebSearch/activity_row_generator.py:166 msgid "Domain skipped" -msgstr "" +msgstr "Alan adı atlandı" #: WebSearch/activity_row_generator.py:167 msgid "Link hidden for object" -msgstr "" +msgstr "Nesne için bağlantı gizlendi" #: WebSearch/activity_row_generator.py:168 msgid "Link hidden for all objects" -msgstr "" +msgstr "Tüm nesneler için bağlantı gizlendi" #: WebSearch/activity_row_generator.py:169 msgid "Attribute updated" -msgstr "" +msgstr "Öznitelik güncellendi" #: WebSearch/activity_row_generator.py:170 msgid "Note updated" @@ -24409,107 +25063,112 @@ msgstr "Not güncellendi" #: WebSearch/constants.py:392 msgid "Column - Icons" -msgstr "" +msgstr "Sütun - Simgeler" #: WebSearch/constants.py:393 msgid "Column - Source Types (flags)" -msgstr "" +msgstr "Sütun - Kaynak Türleri (bayraklar)" #: WebSearch/constants.py:394 msgid "Column - Keys" -msgstr "" +msgstr "Sütun - Anahtarlar" #: WebSearch/constants.py:395 msgid "Column - Title" -msgstr "" +msgstr "Sütun - Başlık" #: WebSearch/constants.py:396 msgid "Column - Website Url" -msgstr "" +msgstr "Sütun - Web Sitesi Url adresi" #: WebSearch/constants.py:397 msgid "Column - Comment" -msgstr "" +msgstr "Sütun - Yorum" #: WebSearch/constants.py:426 msgid "Icon - Visited URLs (checkmark)" -msgstr "" +msgstr "Simge - Ziyaret Edilen URL adresleri (onay işareti)" #: WebSearch/constants.py:427 msgid "Icon - Saved URLs (floppy disk)" -msgstr "" +msgstr "Simge - Kaydedilmiş URL adresleri (disket)" #: WebSearch/constants.py:428 msgid "Icon - URLs linked to UID attributes (UID badge)" -msgstr "" +msgstr "Simge - UID özniteliklerine bağlı URL adresleri (UID rozeti)" #: WebSearch/constants.py:429 msgid "Icon - URLs from regional CSV files (flag)" -msgstr "" +msgstr "Simge - Bölgesel CSV dosyalarından URL adresleri (bayrak)" #: WebSearch/constants.py:430 msgid "Icon - URLs from static CSV files (red pin)" -msgstr "" +msgstr "Simge - Statik CSV dosyalarından URL adresleri (kırmızı pin)" #: WebSearch/constants.py:431 msgid "Icon - URLs from common CSV files (earth)" -msgstr "" +msgstr "Simge - Yaygın CSV dosyalarından URL adresleri (dünya)" #: WebSearch/constants.py:432 msgid "Icon - URLs from cross CSV files (shuffle arrows)" -msgstr "" +msgstr "Simge - Çapraz CSV dosyalarından URL adresleri (karıştırma okları)" #: WebSearch/constants.py:433 msgid "Icon - URLs from custom user directory (spreadsheet icon)" msgstr "" +"Simge - Özel kullanıcı dizininden URL adresleri (elektronik tablo simgesi)" #: WebSearch/constants.py:434 msgid "Icon - URLs from the 'Attributes' tab ('A' icon)" -msgstr "" +msgstr "Simge - 'Öznitelikler' sekmesindeki URL adresleri ('A' simgesi)" #: WebSearch/constants.py:435 msgid "Icon - URLs from the 'Internet' tab ('I' icon)" -msgstr "" +msgstr "Simge - 'İnternet' sekmesindeki URL adresleri ('I' simgesi)" #: WebSearch/constants.py:436 msgid "Icon - URLs from the 'Notes' tab ('N' icon)" -msgstr "" +msgstr "Simge - 'Notlar' sekmesindeki URL adresleri ('N' simgesi)" #: WebSearch/info_panel.py:108 msgid "## 🧩 About WebSearch" -msgstr "" +msgstr "## 🧩 Web Arama Hakkında" #: WebSearch/info_panel.py:111 msgid "" "WebSearch is a Gramplet for Gramps that helps you search genealogy-related " "websites." msgstr "" +"Web Arama, Gramps için soybilimle ilgili web sitelerinde arama yapmanıza " +"yardımcı olan bir Gramplet uygulamasıdır." #: WebSearch/info_panel.py:117 msgid "" "It supports CSV-based link templates, AI-assisted site discovery, and direct " "integration with notes and attributes." msgstr "" +"CSV tabanlı bağlantı şablonlarını, yapay zekâ destekli site keşfini ve " +"notlar ile özniteliklerle doğrudan entegrasyonu destekler." #: WebSearch/info_panel.py:125 msgid "## ⚙️ System Information" -msgstr "" +msgstr "## ⚙️ Sistem Bilgileri" #: WebSearch/info_panel.py:128 msgid "🔻 **Missing:** `qrcode`" -msgstr "" +msgstr "🔻 **Eksik:** `qrcode`" #: WebSearch/info_panel.py:130 msgid "ℹ️ This Python library is not available in your system." -msgstr "" +msgstr "ℹ️ Bu Python kütüphanesi sisteminizde mevcut değil." #: WebSearch/info_panel.py:133 msgid "Without it, QR code generation will not work." -msgstr "" +msgstr "Bu olmadan, QR kodu oluşturma çalışmayacaktır." #: WebSearch/info_panel.py:136 msgid "💡 Usually installed with: `pip install qrcode[pil]`" -msgstr "" +msgstr "💡 Genellikle şu komutla yüklenir: `pip install qrcode[pil]`" #: WebSearch/info_panel.py:140 WebSearch/info_panel.py:162 #: WebSearch/info_panel.py:181 @@ -24517,66 +25176,74 @@ msgid "" "*Note: Some operating systems or environments may require alternative " "installation methods.*" msgstr "" +"*Not: Bazı işletim sistemleri veya ortamlar alternatif yükleme yöntemleri " +"gerektirebilir.*" #: WebSearch/info_panel.py:147 msgid "🔻 **Missing:** `openai`" -msgstr "" +msgstr "🔻 **Eksik:** `openai`" #: WebSearch/info_panel.py:149 msgid "ℹ️ This library is required for accessing OpenAI-based features." -msgstr "" +msgstr "ℹ️ Bu kütüphane, OpenAI tabanlı özelliklere erişmek için gereklidir." #: WebSearch/info_panel.py:153 msgid "" "Without it, AI-generated site suggestions and place history will be disabled." msgstr "" +"Bu olmadan, yapay zekâ tarafından oluşturulan site önerileri ve yer geçmişi " +"devre dışı bırakılacaktır." #: WebSearch/info_panel.py:158 msgid "💡 Usually installed with: `pip install openai`" -msgstr "" +msgstr "💡 Genellikle şu komutla yüklenir: `pip install openai`" #: WebSearch/info_panel.py:169 msgid "🔻 **Missing:** `requests`" -msgstr "" +msgstr "🔻 **Eksik:** `requests`" #: WebSearch/info_panel.py:171 msgid "ℹ️ This library is used to communicate with web APIs." -msgstr "" +msgstr "ℹ️ Bu kütüphane, web API'leriyle iletişim kurmak için kullanılır." #: WebSearch/info_panel.py:174 msgid "Without it, external data sources may not be accessible." -msgstr "" +msgstr "Bu olmadan, harici veri kaynaklarına erişilemeyebilir." #: WebSearch/info_panel.py:177 msgid "💡 Usually installed with: `pip install requests`" -msgstr "" +msgstr "💡 Genellikle şu komutla yüklenir: `pip install requests`" #: WebSearch/info_panel.py:188 msgid "## 📂 Data File Locations" -msgstr "" +msgstr "## 📂 Veri Dosyası Konumları" #: WebSearch/info_panel.py:191 msgid "" "Below are the paths to system and user-defined data files used by WebSearch." msgstr "" +"Aşağıda, Web Arama tarafından kullanılan sistem ve kullanıcı tanımlı veri " +"dosyalarının yolları bulunmaktadır." #: WebSearch/info_panel.py:195 msgid "#### CSV File Paths" -msgstr "" +msgstr "#### CSV Dosya Yolları" #: WebSearch/info_panel.py:197 #, python-format msgid "- **System path:** `{dir|%s}` – contains the built-in CSV files" -msgstr "" +msgstr "- **Sistem yolu:** `{dir|%s}` – yerleşik CSV dosyalarını içerir" #: WebSearch/info_panel.py:201 #, python-format msgid "- **User-defined path:** `{dir|%s}` – for custom user-defined CSV files" msgstr "" +"- **Kullanıcı tanımlı yol:** `{dir|%s}` – özel kullanıcı tanımlı CSV " +"dosyaları içindir" #: WebSearch/info_panel.py:205 msgid "#### JSON File Paths" -msgstr "" +msgstr "#### JSON Dosya Yolları" #: WebSearch/info_panel.py:208 #, python-format @@ -24584,6 +25251,8 @@ msgid "" "- **System path:** `{dir|%s}` – contains the built-in attribute_mapping.json " "file" msgstr "" +"- **Sistem yolu:** `{dir|%s}` – yerleşik attribute_mapping.json dosyasını " +"içerir" #: WebSearch/info_panel.py:214 #, python-format @@ -24591,49 +25260,55 @@ msgid "" "- **User-defined path:** `{dir|%s}` – for custom user-defined " "attribute_mapping.json file" msgstr "" +"- **Kullanıcı tanımlı yol:** `{dir|%s}` – özel kullanıcı tanımlı " +"attribute_mapping.json dosyası içindir" #: WebSearch/info_panel.py:221 msgid " 💡 *Tip: click any path above to open it in your file manager.*" msgstr "" +" 💡 *İpucu: Dosya yöneticinizde açmak için yukarıdaki herhangi bir yola " +"tıklayın.*" #: WebSearch/info_panel.py:224 msgid "📖 View detailed usage in: " -msgstr "" +msgstr "📖 Ayrıntılı kullanım bilgilerini görüntüleyin: " #: WebSearch/info_panel.py:233 msgid "## 💬 Support" -msgstr "" +msgstr "## 💬 Destek" #: WebSearch/info_panel.py:234 msgid "👤 Created and maintained by Yurii Liubymyi" -msgstr "" +msgstr "👤 Yurii Liubymyi tarafından oluşturuldu ve bakımı yapıldı" #: WebSearch/info_panel.py:237 msgid "" "💬 For help or feedback, feel free to mention `@Urchello` on the Gramps " "forum:" msgstr "" +"💬 Yardım veya geri bildirim için Gramps forumunda `@Urchello` etiketini " +"kullanabilirsiniz:" #: WebSearch/info_panel.py:241 msgid "Gramps Forum (Discourse)" -msgstr "" +msgstr "Gramps Forumu (Tartışma)" #: WebSearch/info_panel.py:245 msgid "✅ Bug reports and feature requests are completely **free of charge**." -msgstr "" +msgstr "✅ Hata raporları ve özellik istekleri tamamen **ücretsizdir**." #: WebSearch/info_panel.py:249 msgid "GitHub Issues" -msgstr "" +msgstr "GitHub Sorunları" #: WebSearch/info_panel.py:252 msgid "Gramps Bug Tracker" -msgstr "" +msgstr "Gramps Hata Takip Sistemi" #: WebSearch/info_panel.py:255 #, python-format msgid "🧩 WebSearch Gramplet version: `%s`" -msgstr "" +msgstr "🧩 Web Arama Gramplet sürümü: `%s`" #: WebSearch/internet_links_loader.py:78 WebSearch/internet_links_loader.py:79 msgid "No title" @@ -24641,15 +25316,15 @@ msgstr "Başlık yok" #: WebSearch/markdown_place_history_formatter.py:52 msgid "No data available." -msgstr "" +msgstr "Veri mevcut değil." #: WebSearch/markdown_place_history_formatter.py:57 msgid "HISTORICAL ADMINISTRATIVE DIVISIONS OF" -msgstr "" +msgstr "TARİHİ İDARİ BÖLÜMLERİ" #: WebSearch/markdown_place_history_formatter.py:58 msgid "Foundation date:" -msgstr "" +msgstr "Kuruluş tarihi:" #: WebSearch/markdown_place_history_formatter.py:60 msgid "Coordinates:" @@ -24657,15 +25332,15 @@ msgstr "Koordinatlar:" #: WebSearch/markdown_place_history_formatter.py:61 msgid "Administrative history" -msgstr "" +msgstr "İdari tarihçe" #: WebSearch/markdown_place_history_formatter.py:62 msgid "Data provided to AI:" -msgstr "" +msgstr "Yapay zekâya sağlanan veriler:" #: WebSearch/markdown_place_history_formatter.py:64 msgid "Full hierarchy" -msgstr "" +msgstr "Tam hiyerarşi" #: WebSearch/markdown_place_history_formatter.py:68 msgid "Place Type:" @@ -24673,192 +25348,196 @@ msgstr "Yer Türü:" #: WebSearch/markdown_place_history_formatter.py:69 msgid "Last Retrieved At:" -msgstr "" +msgstr "Son Erişim Tarihi:" #: WebSearch/note_links_loader.py:72 msgid "Note Link (parsed)" -msgstr "" +msgstr "Not Bağlantısı (ayrıştırılmış)" #: WebSearch/note_links_loader.py:125 msgid "Note Link (internal)" -msgstr "" +msgstr "Not Bağlantısı (dahili)" #: WebSearch/note_links_loader.py:128 msgid "Note Link (external)" -msgstr "" +msgstr "Not Bağlantısı (harici)" #: WebSearch/qr_window.py:75 msgid "QR-code" -msgstr "" +msgstr "QR kodu" #: WebSearch/qr_window.py:106 WebSearch/qr_window.py:109 msgid "⚠ Missing dependency \"qrcode\"" -msgstr "" +msgstr "⚠ \"qrcode\" bağımlılığı eksik" #: WebSearch/qr_window.py:120 msgid "" "⚠ Error generating QR code:\n" "Original error: “{}”" msgstr "" +"⚠ QR kodu oluşturma hatası:\n" +"Orijinal hata: “{}”" #: WebSearch/settings_ui_manager.py:112 msgid "Middle Name Handling" -msgstr "" +msgstr "İkinci Ad İşleme" #: WebSearch/settings_ui_manager.py:117 msgid "Leave alone" -msgstr "" +msgstr "Olduğu gibi bırak" #: WebSearch/settings_ui_manager.py:118 msgid "Separate" -msgstr "" +msgstr "Ayır" #: WebSearch/settings_ui_manager.py:128 msgid "URL Compactness Level" -msgstr "" +msgstr "URL Kompaktlık Seviyesi" #: WebSearch/settings_ui_manager.py:134 msgid "Shortest - No Prefix, No Keys" -msgstr "" +msgstr "En Kısa - Ön Ek Yok, Anahtar Yok" #: WebSearch/settings_ui_manager.py:137 msgid "Compact - No Prefix, Keys Without Attributes" -msgstr "" +msgstr "Kompakt - Ön Ek Yok, Özniteliksiz Anahtarlar" #: WebSearch/settings_ui_manager.py:140 msgid "Compact - No Prefix, Keys With Attributes" -msgstr "" +msgstr "Kompakt - Önek Yok, Öznitelikli Anahtarlar" #: WebSearch/settings_ui_manager.py:143 msgid "Long - Without Prefix on the Left" -msgstr "" +msgstr "Uzun - Solda Önek Olmadan" #: WebSearch/settings_ui_manager.py:150 msgid "URL Prefix Replacement" -msgstr "" +msgstr "URL Öneki Değiştirme" #: WebSearch/settings_ui_manager.py:157 msgid "AI Provider" -msgstr "" +msgstr "Yapay Zekâ Sağlayıcısı" #: WebSearch/settings_ui_manager.py:162 msgid "(Disabled)" -msgstr "" +msgstr "(Devre Dışı)" #: WebSearch/settings_ui_manager.py:163 msgid "OpenAI" -msgstr "" +msgstr "OpenAI" #: WebSearch/settings_ui_manager.py:164 msgid "Mistral AI" -msgstr "" +msgstr "Mistral AI" #: WebSearch/settings_ui_manager.py:171 msgid "OpenAI API Key" -msgstr "" +msgstr "OpenAI API Anahtarı" #: WebSearch/settings_ui_manager.py:176 msgid "OpenAI Model" -msgstr "" +msgstr "OpenAI Modeli" #: WebSearch/settings_ui_manager.py:181 msgid "Mistral API Key" -msgstr "" +msgstr "Mistral API Anahtarı" #: WebSearch/settings_ui_manager.py:186 msgid "Mistral Model" -msgstr "" +msgstr "Mistral Modeli" #: WebSearch/settings_ui_manager.py:191 msgid "Show Links From the 'Attributes' tab" -msgstr "" +msgstr "'Öznitelikler' sekmesindeki bağlantıları göster" #: WebSearch/settings_ui_manager.py:196 msgid "Show Links From the 'Internet' tab" -msgstr "" +msgstr "'İnternet' sekmesindeki bağlantıları göster" #: WebSearch/settings_ui_manager.py:201 msgid "Show Links From the Notes" -msgstr "" +msgstr "Notlardaki bağlantıları göster" #: WebSearch/settings_ui_manager.py:209 msgid "Enable AI-generated historical place data" -msgstr "" +msgstr "Yapay zekâ tarafından oluşturulan tarihsel yer verilerini etkinleştir" #: WebSearch/settings_ui_manager.py:215 msgid "Custom country code for AI notes (optional)" -msgstr "" +msgstr "Yapay zekâ notları için özel ülke kodu (isteğe bağlı)" #: WebSearch/settings_ui_manager.py:244 msgid "Enable CSV Files" -msgstr "" +msgstr "CSV dosyalarını etkinleştir" #: WebSearch/settings_ui_manager.py:251 msgid "Display Columns" -msgstr "" +msgstr "Sütunları görüntüle" #: WebSearch/settings_ui_manager.py:265 msgid "Display Icons" -msgstr "" +msgstr "Simgeleri görüntüle" #: WordleGramplet/WordleGramplet.gpr.py:4 #: WordleGramplet/WordleGramplet.gpr.py:10 msgid "Wordle" -msgstr "" +msgstr "Wordle" #: WordleGramplet/WordleGramplet.gpr.py:13 msgid "Gramplet used to make word clouds with wordle.net" -msgstr "" +msgstr "wordle.net ile kelime bulutları oluşturmak için kullanılan Gramplet" #: WordleGramplet/WordleGramplet.py:126 msgid "[Missing]" -msgstr "" +msgstr "[Eksik]" #: WordleGramplet/WordleGramplet.py:140 msgid "Number of font sizes" -msgstr "" +msgstr "Yazı tipi boyutu sayısı" #: WordleGramplet/WordleGramplet.py:144 msgid "Select filter to restrict list" -msgstr "" +msgstr "Listeyi kısıtlamak için filtre seçin" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." -msgstr "" +msgstr "Veritabanına ve gen.lib'e genel erişim için bir kütüphane sağlar." #: libaccess/libaccess.py:222 msgid "libaccess edit name" -msgstr "" +msgstr "libaccess adını düzenle" #: libaccess/libaccess.py:264 msgid "libaccess edit person" -msgstr "" +msgstr "libaccess kişiyi düzenle" #: libwebconnect/libwebconnect.gpr.py:11 msgid "Library for web site collections" -msgstr "" +msgstr "Web sitesi koleksiyonları için kütüphane" #: lxml/etreeGramplet.gpr.py:10 lxml/etreeGramplet.gpr.py:20 msgid "etree" -msgstr "" +msgstr "etree" #: lxml/etreeGramplet.gpr.py:11 msgid "Gramplet for testing etree with Gramps XML" -msgstr "" +msgstr "Gramps XML ile etree'yi test etmek için Gramplet" #: lxml/etreeGramplet.py:83 lxml/lxmlGramplet.py:115 msgid "Invalid timestamp" -msgstr "" +msgstr "Geçersiz zaman damgası" #: lxml/etreeGramplet.py:130 lxml/lxmlGramplet.py:173 msgid "" "Select a Gramps XML file and\n" " click on the Run button." msgstr "" +"Bir Gramps XML dosyası seçin ve\n" +" çalıştır düğmesine tıklayın." #: lxml/etreeGramplet.py:155 lxml/lxmlGramplet.py:197 msgid "No file loaded..." -msgstr "" +msgstr "Hiçbir dosya yüklenmedi..." #: lxml/etreeGramplet.py:203 lxml/etreeGramplet.py:209 msgid "Number of additions and modifications back" @@ -24866,43 +25545,43 @@ msgstr "Geriye dönük ekleme ve değişiklik sayısı" #: lxml/etreeGramplet.py:205 lxml/etreeGramplet.py:210 msgid "Print more informations on console" -msgstr "" +msgstr "Konsolda daha fazla bilgi yazdırın" #: lxml/etreeGramplet.py:254 #, python-format msgid "Cannot uncompress \"%s\"" -msgstr "" +msgstr "\"%s\" sıkıştırması açılamıyor" #: lxml/etreeGramplet.py:254 lxml/lxmlGramplet.py:334 msgid "Is it a compressed .gramps?" -msgstr "" +msgstr "Sıkıştırılmış bir .gramps dosyası mı?" #: lxml/etreeGramplet.py:265 #, python-format msgid "Cannot copy \"%s\"" -msgstr "" +msgstr "\"%s\" kopyalanamıyor" #: lxml/etreeGramplet.py:265 lxml/lxmlGramplet.py:348 msgid "Is it a .gramps?" -msgstr "" +msgstr "Bir .gramps dosyası mı?" #: lxml/etreeGramplet.py:282 msgid "Sorry, no support for your OS yet!" -msgstr "" +msgstr "Üzgünüz, işletim sisteminiz henüz desteklenmiyor!" #: lxml/etreeGramplet.py:296 lxml/lxmlGramplet.py:420 #, python-format msgid "Cannot parse content of \"%(file)s\"" -msgstr "" +msgstr "\"%(file)s\" içeriği ayrıştırılamıyor" #: lxml/etreeGramplet.py:296 lxml/lxmlGramplet.py:420 msgid "Parsing issue" -msgstr "" +msgstr "Ayrıştırma sorunu" #: lxml/etreeGramplet.py:498 #, python-format msgid "XML: Last %s additions and modifications since %s, were on :\n" -msgstr "" +msgstr "XML: %s tarihinden bu yana son %s ekleme ve değişiklik şunlardır :\n" #: lxml/etreeGramplet.py:554 #, python-format @@ -24911,6 +25590,9 @@ msgid "" "XML: Number of records and relations : \t%s\n" "\n" msgstr "" +"\n" +"XML: Kayıt ve ilişki sayısı : \t%s\n" +"\n" #: lxml/etreeGramplet.py:557 #, python-format @@ -24918,6 +25600,8 @@ msgid "" "Number of tags : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Etiket sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:559 #, python-format @@ -24925,6 +25609,8 @@ msgid "" "Number of tags : \n" "\t\t\t%06s\n" msgstr "" +"Etiket sayısı : \n" +"\t\t\t%06s\n" #: lxml/etreeGramplet.py:561 #, python-format @@ -24932,6 +25618,8 @@ msgid "" "Number of events : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Etkinlik sayısı \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:564 #, python-format @@ -24939,6 +25627,8 @@ msgid "" "Number of persons : \n" "\t\t\t%06s\t|\t(%06s) and (%06s)* surnames\n" msgstr "" +"Kişi sayısı : \n" +"\t\t\t%06s\t|\t(%06s) ve (%06s)* soyadları\n" #: lxml/etreeGramplet.py:566 #, python-format @@ -24946,6 +25636,8 @@ msgid "" "Number of persons : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Kişi sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:567 #, python-format @@ -24953,6 +25645,8 @@ msgid "" "Number of families : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Aile sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:568 #, python-format @@ -24960,6 +25654,8 @@ msgid "" "Number of sources : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Kaynak sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:570 #, python-format @@ -24967,6 +25663,8 @@ msgid "" "Number of citations : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Alıntı sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:573 #, python-format @@ -24974,6 +25672,8 @@ msgid "" "Number of places : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Yer sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:574 #, python-format @@ -24981,6 +25681,8 @@ msgid "" "Number of media objects : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Medya nesnesi sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:575 #, python-format @@ -24988,6 +25690,8 @@ msgid "" "Number of repositories : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Depo sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:576 #, python-format @@ -24995,6 +25699,8 @@ msgid "" "Number of notes : \n" "\t\t\t%06s\t|\t(%06s)*\n" msgstr "" +"Not sayısı : \n" +"\t\t\t%06s\t|\t(%06s)*\n" #: lxml/etreeGramplet.py:581 #, python-format @@ -25002,6 +25708,8 @@ msgid "" "\n" "XML: Number of additional records and relations: \t%s\n" msgstr "" +"\n" +"XML: Ek kayıt ve ilişkilerin sayısı: \t%s\n" #: lxml/etreeGramplet.py:585 #, python-format @@ -25014,31 +25722,31 @@ msgstr "" #: lxml/lxmlGramplet.gpr.py:10 lxml/lxmlGramplet.gpr.py:20 msgid "lxml" -msgstr "" +msgstr "lxml" #: lxml/lxmlGramplet.gpr.py:11 msgid "Gramplet for testing lxml and XSLT" -msgstr "" +msgstr "lxml ve XSLT testleri için Gramplet" #: lxml/lxmlGramplet.py:72 msgid "\"gzip\" is missing" -msgstr "" +msgstr "\"gzip\" eksik" #: lxml/lxmlGramplet.py:72 msgid "Where is gzip?" -msgstr "" +msgstr "gzip nerede?" #: lxml/lxmlGramplet.py:94 msgid "Missing python3 lxml" -msgstr "" +msgstr "python3 lxml eksik" #: lxml/lxmlGramplet.py:94 msgid "Please, try to install \"python3 lxml\" package." -msgstr "" +msgstr "Lütfen \"python3 lxml\" paketini yüklemeyi deneyin." #: lxml/lxmlGramplet.py:255 lxml/lxmlGramplet.py:273 lxml/lxmlGramplet.py:749 msgid "xmllint options" -msgstr "" +msgstr "xmllint seçenekleri" #: lxml/lxmlGramplet.py:264 lxml/lxmlGramplet.py:282 msgid "debug places" @@ -25046,65 +25754,65 @@ msgstr "hata ayıklama yerleri" #: lxml/lxmlGramplet.py:265 lxml/lxmlGramplet.py:283 msgid "debug xml" -msgstr "" +msgstr "xml hata ayıklama" #: lxml/lxmlGramplet.py:382 msgid "XSD validation (lxml)" -msgstr "" +msgstr "XSD doğrulaması (lxml)" #: lxml/lxmlGramplet.py:390 #, python-format msgid "xmllint: skip DTD validation for \"%(file)s\"" -msgstr "" +msgstr "xmllint: \"%(file)s\" için DTD doğrulamasını atla" #: lxml/lxmlGramplet.py:407 #, python-format msgid "xmllint: skip RelaxNG validation for \"%(file)s\"" -msgstr "" +msgstr "xmllint: \"%(file)s\" için RelaxNG doğrulamasını atla" #: lxml/lxmlGramplet.py:423 msgid "Custom \"test.xml\" file" -msgstr "" +msgstr "Özel \"test.xml\" dosyası" #: lxml/lxmlGramplet.py:423 msgid "Please try to fix \"test.xml\"" -msgstr "" +msgstr "Lütfen \"test.xml\" dosyasını düzeltmeyi deneyin" #: lxml/lxmlGramplet.py:432 #, python-format msgid "Cannot validate \"%(file)s\" via RelaxNG schema" -msgstr "" +msgstr "\"%(file)s\" RelaxNG şeması üzerinden doğrulanamıyor" #: lxml/lxmlGramplet.py:432 msgid "RelaxNG validation" -msgstr "" +msgstr "RelaxNG doğrulaması" #: lxml/lxmlGramplet.py:445 #, python-format msgid "Cannot parse \"%(file)s\" via etree" -msgstr "" +msgstr "\"%(file)s\" etree üzerinden ayrıştırılamıyor" #: lxml/lxmlGramplet.py:445 msgid "File issue" -msgstr "" +msgstr "Dosya sorunu" #: lxml/lxmlGramplet.py:484 msgid "Parsing file..." -msgstr "" +msgstr "Dosya ayrıştırılıyor..." #: lxml/lxmlGramplet.py:569 #, python-format msgid " - (%(lang)s)" -msgstr "" +msgstr " - (%(lang)s)" #: lxml/lxmlGramplet.py:572 #, python-format msgid " - (? or %(lang)s)" -msgstr "" +msgstr " - (? veya %(lang)s)" #: lxml/lxmlGramplet.py:609 msgid "Missing header" -msgstr "" +msgstr "Üst bilgi eksik" #: lxml/lxmlGramplet.py:609 lxml/lxmlGramplet.py:719 msgid "" @@ -25113,114 +25821,118 @@ msgid "" "Please, try to use a .gramps\n" "generated by Gramps 6.x." msgstr "" +"Geçerli bir .gramps değil.\n" +"Gramplet çalıştırılamıyor...\n" +"Lütfen, Gramps 6.x tarafından oluşturulan\n" +"bir .gramps kullanmayı deneyin." #: lxml/lxmlGramplet.py:622 lxml/lxmlGramplet.py:627 lxml/lxmlGramplet.py:632 #: lxml/lxmlGramplet.py:637 msgid "0" -msgstr "" +msgstr "0" #: lxml/lxmlGramplet.py:652 msgid "File parsed with" -msgstr "" +msgstr "Dosya şu şekilde ayrıştırıldı" #: lxml/lxmlGramplet.py:654 msgid " by Gramps " -msgstr "" +msgstr " Gramps tarafından " #: lxml/lxmlGramplet.py:654 msgid "File was generated on " -msgstr "" +msgstr "Dosya şu tarihte oluşturuldu " #: lxml/lxmlGramplet.py:656 msgid "Period: " -msgstr "" +msgstr "Dönem: " #: lxml/lxmlGramplet.py:659 lxml/lxmlGramplet.py:663 #, python-brace-format msgid "\t{number} surname" msgid_plural "\t{number} surnames; no frequency yet" -msgstr[0] "" +msgstr[0] "\t{number} soyadı; henüz sıklık yok" #: lxml/lxmlGramplet.py:659 #, python-brace-format msgid "\t{number} surname; no frequency yet" -msgstr "" +msgstr "\t{number} soyadı; henüz sıklık yok" #: lxml/lxmlGramplet.py:660 lxml/lxmlGramplet.py:667 #, python-brace-format msgid "\t{number} place" msgid_plural "\t{number} places" -msgstr[0] "" +msgstr[0] "\t{number} yer" #: lxml/lxmlGramplet.py:661 lxml/lxmlGramplet.py:671 #, python-brace-format msgid "\t{number} note" msgid_plural "\t{number} notes" -msgstr[0] "" +msgstr[0] "\t{number} not" #: lxml/lxmlGramplet.py:675 #, python-brace-format msgid "\t{number} source" msgid_plural "\t{number} sources" -msgstr[0] "" +msgstr[0] "\t{number} kaynak" #: lxml/lxmlGramplet.py:694 lxml/lxmlGramplet.py:919 msgid "Gallery.html" -msgstr "" +msgstr "Gallery.html" #: lxml/lxmlGramplet.py:695 #, python-format msgid "1. Has generated a media index on \"%(file)s\".\n" -msgstr "" +msgstr "1. \"%(file)s\" üzerinde bir medya dizini oluşturuldu.\n" #: lxml/lxmlGramplet.py:719 msgid "XML SyntaxError" -msgstr "" +msgstr "XML Sözdizimi Hatası" #: lxml/lxmlGramplet.py:723 msgid "Matches XSD schema." -msgstr "" +msgstr "XSD şemasıyla eşleşiyor." #: lxml/lxmlGramplet.py:763 msgid "xmllint: skip DTD validation" -msgstr "" +msgstr "xmllint: DTD doğrulamasını atla" #: lxml/lxmlGramplet.py:787 msgid "I am looking at ..." -msgstr "" +msgstr "Şuna bakıyorum..." #: lxml/lxmlGramplet.py:788 msgid "Content generated by Gramps" -msgstr "" +msgstr "Gramps tarafından oluşturulan içerik" #: lxml/lxmlGramplet.py:791 msgid "List of sources" -msgstr "" +msgstr "Kaynakların listesi" #: lxml/lxmlGramplet.py:810 msgid "Australia" -msgstr "" +msgstr "Avustralya" #: lxml/lxmlGramplet.py:811 msgid "Brazil" -msgstr "" +msgstr "Brezilya" #: lxml/lxmlGramplet.py:822 msgid "India" -msgstr "" +msgstr "Hindistan" #: lxml/lxmlGramplet.py:824 msgid "Norway" -msgstr "" +msgstr "Norveç" #: lxml/lxmlGramplet.py:825 msgid "Portugal" -msgstr "" +msgstr "Portekiz" #: lxml/lxmlGramplet.py:896 #, python-format msgid "2. Has generated \"%s\".\n" -msgstr "" +msgstr "2. \"%s\" oluşturuldu.\n" #: lxml/lxmlGramplet.py:898 #, python-format @@ -25229,6 +25941,9 @@ msgid "" " \"%s\"\n" " into your preferred web navigator ..." msgstr "" +"Tercih ettiğiniz\n" +" \"%s\"\n" +" web gezgininde açmayı deneyin..." #~ msgid "Primary Name" #~ msgstr "Birincil ad" From efc4d02b0f8d0ed3978521fcd488f8b139054889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirko=20Leonh=C3=A4user?= Date: Wed, 5 Aug 2026 20:02:08 +0200 Subject: [PATCH 098/156] Translated using Weblate (German) Currently translated at 100.0% (5549 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/de/ --- po/de.po | 246 +++++++++++++++++++++++-------------------------------- 1 file changed, 102 insertions(+), 144 deletions(-) diff --git a/po/de.po b/po/de.po index 69aca78c9..44b0b356a 100644 --- a/po/de.po +++ b/po/de.po @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-20 17:01+0000\n" +"PO-Revision-Date: 2026-07-05 22:48+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German \n" @@ -33,7 +33,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -16009,14 +16009,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Grampsversion" +msgstr "Gramps-Assistent" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "KI-Assistent zur Abfrage deines Gramps-Stammbaums" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -16026,29 +16024,35 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Du bist ein hilfsbereiter Genealogie-Assistent mit Zugriff auf die Gramps-" +"Datenbank des Benutzers. Beantworte Fragen zu Personen, Familien, " +"Ereignissen und Verwandtschaftsverhältnissen. Wenn du Informationen aus der " +"Datenbank benötigst, nutze die bereitgestellten Werkzeuge – schreibe niemals " +"Code, simuliere keine Ergebnisse und erfinde keine Daten. Falls es für die " +"angeforderten Informationen kein Werkzeug gibt, gib dies klar und deutlich " +"an." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Zusätzliche Stileinstellungen:" +msgstr "Gramps Assistent-Einstellungen" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Kommunikation und Kontext leeren" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Grampsversion" +msgstr "Gramps-Assistent:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Frag mich alles, was du über das Programm Gramps oder deinen persönlichen " +"Gramps-Stammbaum wissen möchtest. Nutze die Schaltfläche ⚙, um die KI zu " +"konfigurieren.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -16056,10 +16060,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Es wurde kein Modell konfiguriert. Bitte klicke auf die Schaltfläche „" +"Einstellungen“, um vor dem Chatten ein Backend und ein Modell auszuwählen.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Ich denke nach..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -16068,95 +16075,97 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"API-Schlüssel-Fehler: Die Umgebungsvariable {var} ist nicht gesetzt oder " +"ungültig. Stelle sie vor dem Start von Gramps ein:\n" +" export {var}=dein-Schlüssel-hier" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"API-Schlüssel-Fehler: Dieser Anbieter benötigt einen API-Schlüssel. Öffne " +"die Einstellungen und gib den Namen der Umgebungsvariablen für deinen API-" +"Schlüssel ein (z. B. OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Fertig!\n" +msgstr "Fertig.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "System Prompt:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Werkzeuge vereinfachen (empfohlen für kleinere/lokale Modelle)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Wenn diese Option aktiviert ist, werden nur die für deine Frage relevanten " +"Werkzeuge an das Modell gesendet. Dies verbessert die Leistung bei kleineren " +"lokalen Modellen." #: GrampsAssistant/grampsassistant.py:1230 -#, fuzzy -#| msgid "Mistral Model" msgid "Use Local Model" -msgstr "Mistral Modell" +msgstr "Lokales Modell verwenden" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"URL eines lokalen OpenAI-kompatiblen Servers. Ollama: http://localhost:11434" +" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "Modellname (bei LM Studio / llama.cpp leer lassen)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Vom lokalen Server anzuforderndes Modell. Erforderlich für Ollama (z. B. " +"llama3.1). Bei LM Studio oder llama.cpp, die das jeweils geladene Modell " +"verwenden, bitte leer lassen." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Modern" +msgstr "Modell:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Gründungsdatum:" +msgstr "Grundlegendes Modell verwenden" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "z. B. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "Name der Umgebungsvariable, die deinen API-Schlüssel enthält." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "API-Schlüssel Umgebungsvariable:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Backend:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "Website-URL" +msgstr "Basis-URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Name des Partners:" +msgstr "Modellname:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -18706,32 +18715,31 @@ msgstr "Wer erforscht/sucht wen?" #: NameSuite/.venv/lib/python3.12/site-packages/mypy/main.py:450 #, python-format msgid "%(prog)s: error: %(message)s\n" -msgstr "" +msgstr "%(prog)s: Fehler: %(message)s\n" #: NameSuite/name_processor.gpr.py:6 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Given and Patronymic Names" -msgstr "Patronymische Namen:" +msgstr "Prüfung der Vornamen und patronymischen Namen" #: NameSuite/name_processor.gpr.py:9 msgid "" "Tools to rename given name, audit and infer patronymic (East Slavic) names." msgstr "" +"Werkzeuge zum Umbenennen von Vornamen sowie zur Überprüfung und Ableitung " +"von Patronymika (ostslawische Namen)." #: NameSuite/name_processor.gpr.py:25 NameSuite/name_processor.gpr.py:37 -#, fuzzy -#| msgid "Patronymic names:" msgid "Patronymic Suggestion" -msgstr "Patronymische Namen:" +msgstr "Patronymischer Vorschlag" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." msgstr "" +"Schlägt während der Navigation in Echtzeit (ostslawische) Patronymnamen vor." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" -msgstr "" +msgstr "Verwendung" #: NameSuite/name_processor/views/gramplet.py:32 #, python-brace-format @@ -18740,238 +18748,188 @@ msgid "" "Suggested: {0}\n" "Based on father: {1}" msgstr "" +"Fehlendes Patronymikum erkannt.\n" +"Vorschlag: {0}\n" +"Basierend auf dem Vater: {1}" #: NameSuite/name_processor/views/gramplet.py:34 msgid "Navigate to an individual to check patronymic status." -msgstr "" +msgstr "Navigiere zu einer Person, um den Status des Patronyms zu überprüfen." #: NameSuite/name_processor/views/gramplet.py:35 -#, fuzzy -#| msgid "No Active Person set." msgid "No active person selected." -msgstr "Keine aktive Person festgelegt." +msgstr "Keine aktive Person ausgewählt." #: NameSuite/name_processor/views/gramplet.py:37 msgid "" "Patronymic inference can't be inferred for non-binary or unknown genders." msgstr "" +"Bei nicht-binären oder unbekannten Geschlechtern kann kein Patronym " +"abgeleitet werden." #: NameSuite/name_processor/views/gramplet.py:40 msgid "Individual already has a recorded patronymic." -msgstr "" +msgstr "Für diese Person ist bereits ein Patronymikum erfasst." #: NameSuite/name_processor/views/gramplet.py:43 msgid "No attached father found in database family records." msgstr "" +"Keine Angaben zu einem Vater in den Familiendaten der Datenbank gefunden." #: NameSuite/name_processor/views/gramplet.py:46 msgid "Father lacks a recorded first name." -msgstr "" +msgstr "Der Vorname des Vaters ist nicht angegeben." #: NameSuite/name_processor/views/gramplet.py:49 msgid "Could not generate valid morphology patterns." -msgstr "" +msgstr "Es konnten keine gültigen Morphologiemuster generiert werden." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "" +msgstr "Patronymikum erfolgreich eingefügt!" #: NameSuite/name_processor/views/gramplet.py:77 -#, fuzzy -#| msgid "🔍 AI Suggestions" msgid "Apply Suggestion" -msgstr "🔍 KI-Vorschläge" +msgstr "Vorschlag anwenden" #: NameSuite/name_processor/views/tool.py:92 msgid "Infer East Slavic Patronymics" -msgstr "" +msgstr "Ostslawische Patronymika ableiten" #: NameSuite/name_processor/views/tool.py:112 -#, fuzzy -#| msgid "Checking Given Names" msgid "Rename Given Names" -msgstr "Überprüfung der Vornamen" +msgstr "Vornamen umbenennen" #: NameSuite/name_processor/views/tool.py:115 -#, fuzzy -#| msgid "Patronymic names:" msgid "Audit Patronymics" -msgstr "Patronymische Namen:" +msgstr "Prüfung der Patronymika" #: NameSuite/name_processor/views/tool_audit_tab.py:68 msgid "Auditing Settings" -msgstr "" +msgstr "Prüfungseinstellungen" #: NameSuite/name_processor/views/tool_audit_tab.py:76 -#, fuzzy -#| msgid "Place of Record" msgid "All Records" -msgstr "Ort der Aufzeichnung" +msgstr "Alle Datensätze" #: NameSuite/name_processor/views/tool_audit_tab.py:77 -#, fuzzy -#| msgid "Male line" msgid "Males Only" -msgstr "Männliche Linie" +msgstr "Nur männlich" #: NameSuite/name_processor/views/tool_audit_tab.py:78 -#, fuzzy -#| msgid "Female line" msgid "Females Only" -msgstr "Weibliche Linie" +msgstr "Nur weiblich" #: NameSuite/name_processor/views/tool_audit_tab.py:82 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules..." -msgstr "Konfigurieren" +msgstr "Regeln konfigurieren..." #: NameSuite/name_processor/views/tool_audit_tab.py:87 msgid "Match Pre-Revolutionary Orthography" -msgstr "" +msgstr "Anpassung an die vorrevolutionäre Rechtschreibung" #: NameSuite/name_processor/views/tool_audit_tab.py:95 -#, fuzzy -#| msgid "Edit tags" msgid "Audit Database" -msgstr "Etiketten bearbeiten" +msgstr "Datenbank überprüfen" #: NameSuite/name_processor/views/tool_audit_tab.py:117 -#, fuzzy -#| msgid "Select the graph direction." msgid "Select All Safe Corrections" -msgstr "Wähle die Richtung des Diagramms aus." +msgstr "Alle sicheren Korrekturen auswählen" #: NameSuite/name_processor/views/tool_audit_tab.py:122 #: NameSuite/name_processor/views/tool_rename_tab.py:116 -#, fuzzy -#| msgid "Apply to selected places" msgid "Apply Selected Corrections" -msgstr "Auf ausgewählte Orte anwenden" +msgstr "Ausgewählte Korrekturen anwenden" #: NameSuite/name_processor/views/tool_audit_tab.py:162 -#, fuzzy -#| msgid "Configure" msgid "Configure Rules" -msgstr "Konfigurieren" +msgstr "Regeln konfigurieren" #: NameSuite/name_processor/views/tool_audit_tab.py:186 #: NameSuite/name_processor/views/tool_rename_tab.py:156 -#, fuzzy -#| msgid "India" msgid "Individual" -msgstr "Indien" +msgstr "Einzelperson" #: NameSuite/name_processor/views/tool_audit_tab.py:190 #: NameSuite/name_processor/views/tool_rename_tab.py:158 -#, fuzzy -#| msgid "Current sort" msgid "Current" -msgstr "Aktuelle Sortierung" +msgstr "Aktuell" #: NameSuite/name_processor/views/tool_audit_tab.py:193 -#, fuzzy -#| msgid "Section" msgid "Correction" -msgstr "Abschnitt" +msgstr "Korrektur" #: NameSuite/name_processor/views/tool_audit_tab.py:200 -#, fuzzy -#| msgid "Configure" msgid "Conf" -msgstr "Konfigurieren" +msgstr "Konf" #: NameSuite/name_processor/views/tool_audit_tab.py:201 -#, fuzzy -#| msgid "Event Year" msgid "Ref Year" -msgstr "Ereignisjahr" +msgstr "Ref. Jahr" #: NameSuite/name_processor/views/tool_audit_tab.py:204 -#, fuzzy -#| msgid "Translation" msgid "Explanation" -msgstr "Übersetzung" +msgstr "Erläuterung" #: NameSuite/name_processor/views/tool_audit_tab.py:243 -#, fuzzy -#| msgid "Completed?" msgid "Audit Complete!" -msgstr "Abgeschlossen?" +msgstr "Prüfung abgeschlossen!" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "Result" msgid "No Results" -msgstr "Ergebnis" +msgstr "Keine Ergebnisse" #: NameSuite/name_processor/views/tool_audit_tab.py:248 -#, fuzzy -#| msgid "No persons found..." msgid "No issues found." -msgstr "Keine Personen gefunden..." +msgstr "Keine Probleme gefunden." #: NameSuite/name_processor/views/tool_rename_tab.py:63 -#, fuzzy -#| msgid "Search result places" msgid "Search and Replace Options" -msgstr "Suchergebnis Orte" +msgstr "Suchen und Ersetzen – Optionen" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Spouse name:" msgid "Source Name:" -msgstr "Name des Partners:" +msgstr "Quellenname:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" -msgstr "" +msgstr "z. B. Иоанн" #: NameSuite/name_processor/views/tool_rename_tab.py:75 -#, fuzzy -#| msgid "Street Name" msgid "Target Name:" -msgstr "Straßenname" +msgstr "Zielname:" #: NameSuite/name_processor/views/tool_rename_tab.py:77 msgid "e.g. Иван" -msgstr "" +msgstr "z.B. Иван" #: NameSuite/name_processor/views/tool_rename_tab.py:80 msgid "Match Mode:" -msgstr "" +msgstr "Auswahlmodus:" #: NameSuite/name_processor/views/tool_rename_tab.py:82 msgid "Exact Match" -msgstr "" +msgstr "Exakte Übereinstimmung" #: NameSuite/name_processor/views/tool_rename_tab.py:83 -#, fuzzy -#| msgid "SubDistrict" msgid "Substring" -msgstr "Unterbezirk" +msgstr "Teilzeichenfolge" #: NameSuite/name_processor/views/tool_rename_tab.py:84 -#, fuzzy -#| msgid "Allow regular expressions." msgid "Regular Expression" -msgstr "Reguläre Ausdrücke zulassen." +msgstr "Regulärer Ausdruck" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Index of Names" msgid "Scan for Names" -msgstr "Namensverzeichnis" +msgstr "Nach Namen suchen" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" -msgstr "" +msgstr "Ursprünglichen Namen als Alternative beibehalten" #: NameSuite/name_processor/views/tool_rename_tab.py:160 -#, fuzzy -#| msgid "Proposed sort" msgid "Proposed" -msgstr "Vorgeschlagene Sortierung" +msgstr "Vorgeschlagen" #: NetworkChart/NetworkChart.gpr.py:24 msgid "Network Chart" From 44f7f176a399952ea575686d31d5c564586c6e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Ekholm?= Date: Wed, 5 Aug 2026 20:02:09 +0200 Subject: [PATCH 099/156] Translated using Weblate (Swedish) Currently translated at 67.7% (3761 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sv/ --- po/sv.po | 258 +++++++++++++++++++++++-------------------------------- 1 file changed, 107 insertions(+), 151 deletions(-) diff --git a/po/sv.po b/po/sv.po index a21264b5b..ce1985ed1 100644 --- a/po/sv.po +++ b/po/sv.po @@ -18,7 +18,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-17 20:01+0000\n" +"PO-Revision-Date: 2026-07-05 22:49+0000\n" "Last-Translator: Pär Ekholm \n" "Language-Team: Swedish \n" @@ -27,7 +27,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -9891,32 +9891,24 @@ msgid "Street etc with No. of House" msgstr "Gata etc. med husnummer" #: Form/form_ie.xml.h:13 -#, fuzzy -#| msgid "Schedule" msgid "No. of Schedule" -msgstr "Schema" +msgstr "Antal scheman" #: Form/form_ie.xml.h:14 msgid "Name of Public Institution" -msgstr "" +msgstr "Namn på offentlig institution" #: Form/form_ie.xml.h:16 Form/form_ie.xml.h:113 -#, fuzzy -#| msgid "Maiden Surname" msgid "Name and Surname" -msgstr "Flicknamn" +msgstr "Namn och efternamn" #: Form/form_ie.xml.h:18 -#, fuzzy -#| msgid "10. Relationship to Head of Family" msgid "Relationship to Head of Household" -msgstr "10. Släktskap med familjeöverhuvud" +msgstr "Förhållande till hushållets överhuvud" #: Form/form_ie.xml.h:19 -#, fuzzy -#| msgid "Default Age" msgid "Year Age" -msgstr "Standardålder" +msgstr "År Ålder" #: Form/form_ie.xml.h:20 Form/form_us.xml.h:2561 msgid "Age in years" @@ -9924,203 +9916,175 @@ msgstr "" #: Form/form_ie.xml.h:21 msgid "Month Age" -msgstr "" +msgstr "Månad Ålder" #: Form/form_ie.xml.h:22 -#, fuzzy -#| msgid "Merge into import" msgid "Age in months" -msgstr "Slå samman till import" +msgstr "Ålder i månader" #: Form/form_ie.xml.h:25 msgid "Marriage or Orphanhood" -msgstr "" +msgstr "Äktenskap eller föräldralöshet" #: Form/form_ie.xml.h:28 msgid "" "\"Irish only\", \"Irish and English\", \"English and Irish\", \"Read but " "cannot speak Irish\"" msgstr "" +"\"Endast irländska\", \"Irländska och engelska\", \"Engelska och " +"irländska\", \"Läser men kan inte tala irländska\"" #: Form/form_ie.xml.h:33 msgid "Years married in present Marriage" -msgstr "" +msgstr "År gifta i nuvarande äktenskap" #: Form/form_ie.xml.h:34 -#, fuzzy -#| msgid "Date Married" msgid "Months Married" -msgstr "Datum för giftermål" +msgstr "Månader gifta" #: Form/form_ie.xml.h:35 msgid "Months married in present Marriage" -msgstr "" +msgstr "Månader gifta i nuvarande äktenskap" #: Form/form_ie.xml.h:36 -#, fuzzy -#| msgid "Parents enclose children" msgid "Present Marriage Children" -msgstr "Föräldrar stänger in barn" +msgstr "Nuvarande äktenskaps barn" #: Form/form_ie.xml.h:37 msgid "Children born alive to present marriage" -msgstr "" +msgstr "Barn födda levande i nuvarande äktenskap" #: Form/form_ie.xml.h:38 -#, fuzzy -#| msgid "Children Living" msgid "Total Children Under 16 Living" -msgstr "Barn som lever" +msgstr "Totalt antal barn under 16 år som lever" #: Form/form_ie.xml.h:39 msgid "(1.) Entry no." -msgstr "" +msgstr "(1.) Inlägg nr." #: Form/form_ie.xml.h:40 Form/form_ie.xml.h:74 Form/form_ie.xml.h:102 msgid "Superintendent Registrar's District" -msgstr "" +msgstr "Chefsregistratorns distrikt" #: Form/form_ie.xml.h:41 Form/form_ie.xml.h:73 Form/form_ie.xml.h:103 msgid "Registrar's District" -msgstr "" +msgstr "Registratorns distrikt" #: Form/form_ie.xml.h:43 Form/form_ie.xml.h:105 msgid "Union" -msgstr "" +msgstr "Union" #: Form/form_ie.xml.h:45 -#, fuzzy -#| msgid "Date of Registration" msgid "(10.) Signature of Registrar" -msgstr "Datum för registrering" +msgstr "(10.) Registratorns underskrift" #: Form/form_ie.xml.h:46 msgid "(11.) Baptismal Name if added after Registration of Birth, and Date" -msgstr "" +msgstr "(11.) Dopnamn om det tillkommit efter födelseregistrering, och datum" #: Form/form_ie.xml.h:48 msgid "(3.) Name (if any)" -msgstr "" +msgstr "(3.) Namn (om sådant finns)" #: Form/form_ie.xml.h:50 Form/form_ie.xml.h:52 -#, fuzzy -#| msgid "3. Place of Birth" msgid "(2.) Date and Place of Birth" -msgstr "3. Födelseort" +msgstr "(2.) Födelsedatum och födelseort" #: Form/form_ie.xml.h:54 msgid "(4.) M or F for male or female" -msgstr "" +msgstr "(4.) M eller K för man eller kvinna" #: Form/form_ie.xml.h:56 Form/form_ie.xml.h:58 msgid "(5.) Name and Surname and Dwelling-place of Father" -msgstr "" +msgstr "(5.) Faderns namn och efternamn samt bostad" #: Form/form_ie.xml.h:60 msgid "(7.) Rank or Profession of Father" -msgstr "" +msgstr "(7.) Faderns rang eller yrke" #: Form/form_ie.xml.h:62 Form/form_ie.xml.h:64 msgid "(6.) Name and Surname and Maiden Surname of Mother" -msgstr "" +msgstr "(6.) Moderns namn och efternamn samt flicknamn" #: Form/form_ie.xml.h:66 Form/form_ie.xml.h:68 msgid "(8.) Signature, Qualification, and Residence of Informant" -msgstr "" +msgstr "(8.) Informantens underskrift, kvalifikationer och bosättningsort" #: Form/form_ie.xml.h:69 msgid "When Registered" -msgstr "" +msgstr "När registrerad" #: Form/form_ie.xml.h:70 msgid "(9.) Date when birth was registered" -msgstr "" +msgstr "(9.) Datum då födelsen registrerades" #: Form/form_ie.xml.h:71 -#, fuzzy -#| msgid "Year only" msgid "Year" -msgstr "Bara år" +msgstr "År" #: Form/form_ie.xml.h:72 msgid "Solemnized at the Catholic" -msgstr "" +msgstr "Högtidlighållande vid den katolska" #: Form/form_ie.xml.h:76 Form/form_ie.xml.h:107 -#, fuzzy -#| msgid "Serial Number" msgid "Certificate number" -msgstr "Serienummer" +msgstr "Certifikatnummer" #: Form/form_ie.xml.h:77 Form/form_ie.xml.h:108 -#, fuzzy -#| msgid "Show page numbers" msgid "Page number" -msgstr "Visa sidnummer" +msgstr "Sidnummer" #: Form/form_ie.xml.h:78 Form/form_ie.xml.h:109 msgid "(1.) Entry No." -msgstr "" +msgstr "(1.) Inlägg nr." #: Form/form_ie.xml.h:79 -#, fuzzy -#| msgid "Date Married" msgid "(2.) When Married" -msgstr "Datum för giftermål" +msgstr "(2.) När gift" #: Form/form_ie.xml.h:81 -#, fuzzy -#| msgid "Maiden Surname" msgid "(3.) Name and Surname" -msgstr "Flicknamn" +msgstr "(3.) Förnamn och efternamn" #: Form/form_ie.xml.h:83 msgid "(4.) Full or Minor" -msgstr "" +msgstr "(4.) Fullständig eller mindre" #: Form/form_ie.xml.h:85 msgid "(5.) Bachelor or Spinster" -msgstr "" +msgstr "(5.) Ungkarl eller ungmö" #: Form/form_ie.xml.h:87 msgid "(6.) Rank or Profession" -msgstr "" +msgstr "(6.) Rang eller yrke" #: Form/form_ie.xml.h:89 -#, fuzzy -#| msgid "Date of Marriage" msgid "Residence at the Time of Marriage" -msgstr "Datum för giftermål" +msgstr "Bostad vid tidpunkten för äktenskapet" #: Form/form_ie.xml.h:91 Form/form_ie.xml.h:95 -#, fuzzy -#| msgid "Father's Name" msgid "(8.) Father's Name and Surname" -msgstr "Fars namn" +msgstr "(8.) Fars namn och efternamn" #: Form/form_ie.xml.h:93 Form/form_ie.xml.h:97 msgid "(9.) Rank or Profession of Father" -msgstr "" +msgstr "(9.) Fars rang eller yrke" #: Form/form_ie.xml.h:110 msgid "(10.) When Registered" -msgstr "" +msgstr "(10.) Vid registrering" #: Form/form_ie.xml.h:111 -#, fuzzy -#| msgid "Date of Registration" msgid "(11.) Signature of Registrar" -msgstr "Datum för registrering" +msgstr "(11.) Registratorns underskrift" #: Form/form_ie.xml.h:114 Form/form_us.xml.h:2455 Form/form_us.xml.h:2494 msgid "Date of Death" msgstr "" #: Form/form_ie.xml.h:115 Form/form_ie.xml.h:117 -#, fuzzy -#| msgid "30. Age and Cause of Deaths" msgid "(2.) Date and Place of Death" -msgstr "30. Ålder och dödsorsak" +msgstr "(2.) Dödsdatum och -ort" #: Form/form_ie.xml.h:116 Form/form_us.xml.h:2432 Form/form_us.xml.h:2495 msgid "Place of Death" @@ -10128,49 +10092,39 @@ msgstr "" #: Form/form_ie.xml.h:119 msgid "(4.) M or F for Male or Female" -msgstr "" +msgstr "(4.) M eller K för man eller kvinna" #: Form/form_ie.xml.h:121 -#, fuzzy -#| msgid "Widowed, divorced" msgid "(5.) Widowed or Married" -msgstr "Änka/änkling, frånskild" +msgstr "(5.) Änka/Änkling eller gift" #: Form/form_ie.xml.h:123 -#, fuzzy -#| msgid "6. Age at Next Birthday" msgid "(6.) Age last Birthday" -msgstr "6. Ålder vid nästa födelsedag" +msgstr "(6.) Ålder senaste födelsedag" #: Form/form_ie.xml.h:125 -#, fuzzy -#| msgid "2. Profession, Trade or Occupation" msgid "(7.) Rank, Profession, or Occupation" -msgstr "2. Yrke, handel eller sysselsättning" +msgstr "(7.) Rang, yrke eller sysselsättning" #: Form/form_ie.xml.h:127 Form/form_ie.xml.h:129 msgid "(8.) Certified Cause of Death and Duration of Illness" -msgstr "" +msgstr "(8.) Bekräftad dödsorsak och sjukdomens varaktighet" #: Form/form_ie.xml.h:128 -#, fuzzy -#| msgid "Direction of time" msgid "Duration of Illness" -msgstr "Tidsriktning" +msgstr "Sjukdomens varaktighet" #: Form/form_ie.xml.h:132 -#, fuzzy -#| msgid "Relation to Head" msgid "Relation to deceased" -msgstr "Släktskap till huvudperson" +msgstr "Släktskap till avliden" #: Form/form_ie.xml.h:133 msgid "(9.) e.g. Son, Daughter" -msgstr "" +msgstr "(9.) t.ex. Son, Dotter" #: Form/form_ie.xml.h:135 msgid "(9.) e.g. Present at death" -msgstr "" +msgstr "(9.) t.ex. Närvarande vid dödsfallet" #: Form/form_pl.xml.h:5 msgid "Event Place (Original)" @@ -15754,14 +15708,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Gramps version" +msgstr "Gramps-assistent" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "AI-assistent för att söka i ditt Gramps-släktträd" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15771,29 +15723,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Du är en hjälpsam släktforskningsassistent med tillgång till användarens " +"Gramps-databas. Svara på frågor om personer, familjer, händelser och " +"släktskap. När du behöver information från databasen, anropa de verktyg som " +"finns tillgängliga – skriv aldrig kod, simulera resultat eller hitta på " +"data. Om det inte finns något verktyg för den begärda informationen, säg det " +"tydligt." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Extra mallinställningar:" +msgstr "Inställningar för Gramps-assistenten" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Tydlig konversation och sammanhang" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Gramps version" +msgstr "Gramps-assistenten:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Fråga mig om vad som helst om Gramps-programmet eller ditt specifika Gramps-" +"släktträd. Använd ⚙-knappen för att konfigurera AI:n.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15801,10 +15757,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Ingen modell konfigurerad. Klicka på knappen Inställningar för att välja en " +"backend och modell innan du chattar.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Tänker..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15813,93 +15772,96 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"API-nyckelfel: miljövariabeln {var} är inte angiven eller är ogiltig. Ställ " +"in den innan du startar Gramps:\n" +" export {var}=din-nyckel-här" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"API-nyckelfel: den här leverantören kräver en API-nyckel. Öppna " +"Inställningar och ange miljövariabelnamnet för din API-nyckel (t.ex. " +"OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Klart!\n" +msgstr "Klart.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Systemprompt:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" -msgstr "" +msgstr "Förenklingsverktyg (rekommenderas för mindre/lokala modeller)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"När det är aktiverat skickas endast de verktyg som är relevanta för din " +"fråga till modellen. Detta förbättrar prestandan med mindre lokala modeller." #: GrampsAssistant/grampsassistant.py:1230 msgid "Use Local Model" -msgstr "" +msgstr "Använd lokal modell" #: GrampsAssistant/grampsassistant.py:1249 msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"URL till en lokal OpenAI-kompatibel server. Ollama: http://localhost:11434 " +"LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "modellnamn (lämna tomt för LM Studio / llama.cpp)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Modell att begära från den lokala servern. Krävs för Ollama (t.ex. llama3.1)" +". Lämna tomt för LM Studio eller llama.cpp, som använder den modell som " +"laddas." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Modern" +msgstr "Modell:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Grunddatum:" +msgstr "Använd grundläggande modell" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "t.ex. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "Namn på miljövariabeln som innehåller din API-nyckel." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "API-nyckelmiljövariabel:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Backend:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "Webbplatsens URL" +msgstr "Bas-URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Namn på make/maka:" +msgstr "Modellnamn:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -15937,25 +15899,19 @@ msgid "Media files are in sync." msgstr "Mediefiler är synkroniserade." #: GrampsWebSync/grampswebsync.py:345 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media file." -#| msgid_plural "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media files." -msgstr "Laddar ner %s mediefil med lyckat resultat." +msgstr "Laddar ner %s mediefiler med lyckat resultat." #: GrampsWebSync/grampswebsync.py:348 -#, fuzzy, python-format -#| msgid "Encountered %s error during download." -#| msgid_plural "Encountered %s errors during download." +#, python-format msgid "Encountered %s errors during download." -msgstr "Mötte %s fel vid nedladdning." +msgstr "%s fel uppstod under nedladdningen." #: GrampsWebSync/grampswebsync.py:354 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media file." -#| msgid_plural "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media files." -msgstr "Laddar upp %s mediefil med lyckat resultat." +msgstr "Laddar upp %s mediefiler med lyckat resultat." #: GrampsWebSync/grampswebsync.py:357 #, fuzzy, python-format From ec5616ebac66af73abfaef48fcb0275c0a595a40 Mon Sep 17 00:00:00 2001 From: Paolo Zamponi Date: Wed, 5 Aug 2026 20:02:09 +0200 Subject: [PATCH 100/156] Translated using Weblate (Italian) Currently translated at 47.7% (2651 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/it/ --- po/it.po | 85 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/po/it.po b/po/it.po index 7615f9d41..832e204f2 100644 --- a/po/it.po +++ b/po/it.po @@ -67,7 +67,7 @@ msgstr "" "Project-Id-Version: gramps 3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-17 20:01+0000\n" +"PO-Revision-Date: 2026-07-09 15:32+0000\n" "Last-Translator: Paolo Zamponi \n" "Language-Team: Italian \n" @@ -76,7 +76,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -1679,7 +1679,7 @@ msgstr "" #: D3Charts/DescendantIndentedTree.py:1920 msgid "URL prefix path." -msgstr "" +msgstr "Percorso del prefisso URL." #: D3Charts/DescendantIndentedTree.py:1922 msgid "URL prefix to apply to each auto-generated HREF link." @@ -3718,10 +3718,8 @@ msgstr "" "Windows/Fonts" #: DescendantsLines/DescendantsLines.py:1623 -#, fuzzy -#| msgid "Database size" msgid "Font base size" -msgstr "Grandezza del database" +msgstr "Dimensione base del carattere" #: DescendantsLines/DescendantsLines.py:1629 msgid "Box around Person's block" @@ -4311,11 +4309,11 @@ msgstr "(ordinato per quantità)" #: DynamicWeb/dynamicweb.py:2920 msgid ": activate to sort column ascending" -msgstr "" +msgstr ": attivare per ordinare la colonna in ordine crescente" #: DynamicWeb/dynamicweb.py:2924 msgid ": activate to sort column descending" -msgstr "" +msgstr ": attivare per ordinare la colonna in ordine decrescente" #: DynamicWeb/dynamicweb.py:2929 msgid "" @@ -4323,6 +4321,9 @@ msgid "" "editor and save as an SVG file.
Make sure that the text editor encoding " "is UTF-8.

" msgstr "" +"

Questa pagina contiene il codice SVG grezzo.
Copiare il contenuto in " +"un editor di testo, e salvarlo come file SVG.
Assicurarsi che la codifica " +"dell'editor di testo sia UTF-8.

" #: DynamicWeb/dynamicweb.py:2937 msgid "Alternate Name" @@ -4346,7 +4347,7 @@ msgstr "Esempi" #: DynamicWeb/dynamicweb.py:2973 msgid "F" -msgstr "" +msgstr "F" #: DynamicWeb/dynamicweb.py:2974 msgid "Families Index" @@ -4420,7 +4421,7 @@ msgstr "Altri partecipanti" #: DynamicWeb/dynamicweb.py:3043 msgid "Person page" -msgstr "" +msgstr "Pagina della persona" #: DynamicWeb/dynamicweb.py:3044 msgid "Person to search for" @@ -6136,9 +6137,8 @@ msgid "The active Person" msgstr "La persona attiva" #: FilterRules/activepersonrule.py:58 -#, fuzzy msgid "Matches the active person" -msgstr "Persona attiva: %s" +msgstr "Corrisponde alla persona attiva" #: FilterRules/ageatdeath.gpr.py:25 FilterRules/ageatdeath.py:90 #: FilterRules/ageatdeath.py:92 @@ -6377,10 +6377,8 @@ msgstr "" #: FilterRules2/xchromdescendants.gpr.py:25 #: FilterRules2/xchromdescendants.py:66 -#, fuzzy -#| msgid "Produces descendants lines of a person" msgid "X-chromosomal descendants of " -msgstr "Produce le linee dei discendenti di una persona" +msgstr "Discendenti con cromosoma X di " #: FilterRules2/xchromdescendants.gpr.py:27 msgid "" @@ -10023,10 +10021,8 @@ msgid "" msgstr "" #: Form/form_ie.xml.h:33 -#, fuzzy -#| msgid "Years at current residence" msgid "Years married in present Marriage" -msgstr "Anni nella residenza attuale" +msgstr "Anni di matrimonio nell'unione attuale" #: Form/form_ie.xml.h:34 msgid "Months Married" @@ -10045,10 +10041,8 @@ msgid "Children born alive to present marriage" msgstr "" #: Form/form_ie.xml.h:38 -#, fuzzy -#| msgid "Children Living" msgid "Total Children Under 16 Living" -msgstr "Figli viventi" +msgstr "Numero totale di minori di 16 anni residenti" #: Form/form_ie.xml.h:39 msgid "(1.) Entry no." @@ -10143,10 +10137,8 @@ msgid "(2.) When Married" msgstr "Data del matrimonio" #: Form/form_ie.xml.h:81 -#, fuzzy -#| msgid "Maiden Surname" msgid "(3.) Name and Surname" -msgstr "Cognome da nubile" +msgstr "(3.) Nome e cognome" #: Form/form_ie.xml.h:83 msgid "(4.) Full or Minor" @@ -10169,10 +10161,8 @@ msgid "(8.) Father's Name and Surname" msgstr "" #: Form/form_ie.xml.h:93 Form/form_ie.xml.h:97 -#, fuzzy -#| msgid "Rank, Profession or Occupation" msgid "(9.) Rank or Profession of Father" -msgstr "Grado, professione o impiego" +msgstr "(9.) Grado o professione del padre" #: Form/form_ie.xml.h:110 msgid "(10.) When Registered" @@ -15213,10 +15203,8 @@ msgid "" msgstr "" #: Form/form_us.xml.h:2567 -#, fuzzy -#| msgid "Citizenship" msgid "Country of Citizenship" -msgstr "Cittadinanza" +msgstr "Nazione di cittadinanza" #: Form/form_us.xml.h:2568 msgid "" @@ -15799,11 +15787,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 msgid "Gramps Assistant" -msgstr "" +msgstr "Assistente di Gramps" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" msgstr "" +"Assistente AI per effettuare ricerche nel tuo albero genealogico di Gramps" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15813,27 +15802,33 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Sei un assistente genealogico disponibile che ha accesso al database Gramps " +"dell'utente. Rispondi alle domande su persone, famiglie, eventi e relazioni. " +"Quando hai bisogno di informazioni dal database, utilizza gli strumenti " +"messi a disposizione: non scrivere mai codice, non simulare risultati, e non " +"inventare dati. Se non esiste uno strumento per le informazioni richieste, " +"dillo chiaramente." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Ulteriori impostazioni sullo stile:" +msgstr "Impostazioni per l'Assistente di Gramps" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Elimina conversazione e contesto" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 msgid "Gramps Assistant:" -msgstr "" +msgstr "Assistente di Gramps:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Chiedimi qualsiasi cosa sul programma Gramps o sul tuo albero genealogico " +"specifico in Gramps. Usa il pulsante ⚙ per configurare l'IA.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15841,10 +15836,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Nessun modello configurato. Fare clic sul pulsante Impostazioni per " +"scegliere un motore e un modello prima di conversare,\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Sto riflettendo..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15853,12 +15851,18 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"Errore relativo alla chiave API: la variabile d'ambiente {var} non è " +"impostata o non è valida. Impostarla prima di avviare Gramps:\n" +" export {var}=your-key-here" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"Errore relativo alla chiave API: questo provider richiede una chiave API. " +"Aprire le Impostazioni e inserire il nome della variabile d'ambiente " +"corrispondente alla chiave API (ad es. OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 #, fuzzy @@ -21152,6 +21156,13 @@ msgid "" "Assistant in order to export all data.\n" "\n" msgstr "" +"Backup in formato XML di Gramps " +"Nelle versioni recenti di Gramps, l'opzione “Crea backup...” si trova nel " +"menu Albero genealogico; in caso contrario, utilizzare “Esporta...” nello " +"stesso menu, ma deselezionando le opzioni relative alla privacy " +"nell'Assistente all'esportazione per poter esportare tutti i dati. \n" +"\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:336 msgid "" From 015e94ba79ee3d6d55856072387e98aafa88aac1 Mon Sep 17 00:00:00 2001 From: Andi Chandler Date: Wed, 5 Aug 2026 20:02:09 +0200 Subject: [PATCH 101/156] Translated using Weblate (English (United Kingdom)) Currently translated at 3.8% (211 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/en_GB/ --- po/en_GB.po | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/po/en_GB.po b/po/en_GB.po index ba3ef8f46..420525e5d 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -29,8 +29,8 @@ msgstr "" "Project-Id-Version: gramps 3.5.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-07-01 19:34+0000\n" -"Last-Translator: FarmYard Gaming \n" +"PO-Revision-Date: 2026-07-11 13:01+0000\n" +"Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom) \n" "Language: en_GB\n" @@ -38,7 +38,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.7.1.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -15549,10 +15549,8 @@ msgid "Base URL:" msgstr "" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Source type" msgid "Model name:" -msgstr "Source type" +msgstr "Model name:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" @@ -17661,7 +17659,7 @@ msgstr "" #: MediaBrowser/MediaBrowser.gpr.py:39 msgid "Browser" -msgstr "" +msgstr "Browser" #: MediaMerge/mediamerge.gpr.py:31 msgid "Merge Media" @@ -18110,10 +18108,8 @@ msgid "Search and Replace Options" msgstr "" #: NameSuite/name_processor/views/tool_rename_tab.py:70 -#, fuzzy -#| msgid "Source type" msgid "Source Name:" -msgstr "Source type" +msgstr "Source Name:" #: NameSuite/name_processor/views/tool_rename_tab.py:72 msgid "e.g. Иоанн" @@ -18144,10 +18140,8 @@ msgid "Regular Expression" msgstr "" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Image for males." msgid "Scan for Names" -msgstr "Image for males." +msgstr "Scan for Names" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" @@ -18884,10 +18878,8 @@ msgid "(Rows only affect multi-row sections such as census household lists.)" msgstr "" #: PDFForms/generatepdfform.py:200 -#, fuzzy -#| msgid "General Data" msgid "Generations:" -msgstr "General Data" +msgstr "Generations:" #: PDFForms/generatepdfform.py:213 msgid "Includes subject plus up to 5 ancestor generations (2–32 people)." @@ -18922,10 +18914,8 @@ msgid "Form not found" msgstr "" #: PDFForms/generatepdfform.py:306 -#, fuzzy -#| msgid "Generation " msgid "Generation failed" -msgstr "Generation " +msgstr "Generation failed" #: PDFForms/generatepdfform.py:317 #, fuzzy @@ -24791,12 +24781,11 @@ msgstr[0] "" msgstr[1] "" #: lxml/lxmlGramplet.py:675 -#, fuzzy, python-brace-format -#| msgid "New source" +#, python-brace-format msgid "\t{number} source" msgid_plural "\t{number} sources" -msgstr[0] "New source" -msgstr[1] "New source" +msgstr[0] "\t{number} source" +msgstr[1] "\t{number} sources" #: lxml/lxmlGramplet.py:694 lxml/lxmlGramplet.py:919 msgid "Gallery.html" From 74674596fe89b8a7ffe6593450c1787cd4da9a24 Mon Sep 17 00:00:00 2001 From: Kaj Arne Mikkelsen Date: Wed, 5 Aug 2026 20:02:10 +0200 Subject: [PATCH 102/156] Translated using Weblate (Danish) Currently translated at 58.1% (3227 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ Translated using Weblate (Danish) Currently translated at 57.7% (3206 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ --- po/da.po | 91 ++++++++++++++++++++------------------------------------ 1 file changed, 33 insertions(+), 58 deletions(-) diff --git a/po/da.po b/po/da.po index a29bb6510..12f134f88 100644 --- a/po/da.po +++ b/po/da.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-04-28 18:11+0000\n" +"PO-Revision-Date: 2026-07-23 14:01+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish \n" @@ -20,7 +20,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.17.1-dev\n" +"X-Generator: Weblate 2026.8.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -137,39 +137,32 @@ msgid "The style used for the title of the page." msgstr "Fontformat for sidens titel" #: AncestryTableReport/AncestryTableReport.gpr.py:3 -#, fuzzy -#| msgid "Ancestry" msgid "Ancestry Table" -msgstr "Aner" +msgstr "Anetabel" #: AncestryTableReport/AncestryTableReport.gpr.py:4 -#, fuzzy -#| msgid "Produces descendants lines of a person" msgid "Produces a table of ancestry about a person" -msgstr "Danner efterkommerlinier for en person" +msgstr "Danner en anetabel for en person" #: AncestryTableReport/AncestryTableReport.py:133 #, python-format msgid "Person %(name)s is not in the Database" -msgstr "" +msgstr "Personen %(name)s findes ikke i databasen" #: AncestryTableReport/AncestryTableReport.py:277 -#, fuzzy, python-format -#| msgid "The style used for names" +#, python-format msgid "Ancestry Table Report for %(name)s" -msgstr "Stilen der benyttes til navne" +msgstr "Anetabel rapport for %(name)s" #: AncestryTableReport/AncestryTableReport.py:311 -#, fuzzy, python-format -#| msgid "Generations up" +#, python-format msgid "Generation %(gen_number)d" -msgstr "Generationer op" +msgstr "Generation %(gen_number)d" #: AncestryTableReport/AncestryTableReport.py:360 -#, fuzzy, python-format -#| msgid "Number of Ancestors found " +#, python-format msgid "Number of Ancestors for %(name)s" -msgstr "Antal fundne aner " +msgstr "Antal aner for %(name)s" #: AncestryTableReport/AncestryTableReport.py:370 #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:54 @@ -180,31 +173,27 @@ msgstr "Generation" #: AncestryTableReport/AncestryTableReport.py:472 msgid "To start a new page after each generation." -msgstr "" +msgstr "At begynde en ny side efter hver generation." #: AncestryTableReport/AncestryTableReport.py:475 -#, fuzzy -#| msgid "Number of ancestors" msgid "Number of ancestors per generation" -msgstr "Antal aner" +msgstr "Antal aner pr generation" #: AncestryTableReport/AncestryTableReport.py:476 -#, fuzzy -#| msgid "Whether to display the theoretical number of ancestor by generation" msgid "Add a page with tne number of ancestors per generation." -msgstr "Hvorvidt det teoretiske anta aber per generation skal medtagesl" +msgstr "Tilføj en side med antallet aner per generation." #: AncestryTableReport/AncestryTableReport.py:495 -#, fuzzy -#| msgid "Color the name to indicate a person's gender in the chart." msgid "Mask the name of the calendar in the dates" -msgstr "Farv navnet for at angive en persons køn i diagrammet." +msgstr "Skjul kalendernavnet i datoerne" #: AncestryTableReport/AncestryTableReport.py:496 msgid "" "By default, except for gregorian dates, Gramps shows the name of the " "calendar in the dates." msgstr "" +"Som standard viser Gramps navnet på kalenderen i datoerne, bortset fra " +"gregorianske datoer." #: AncestryTableReport/AncestryTableReport.py:519 #: RepositoriesReport/RepositoriesReport.py:175 @@ -214,74 +203,60 @@ msgid "The style used for the title of the report." msgstr "Stilen der benyttes til rapportens titel." #: AncestryTableReport/AncestryTableReport.py:541 -#, fuzzy -#| msgid "The style used for the subtitle of the report." msgid "The style used for the Sosa number of the paternal branch." -msgstr "Stilen der benyttes til rapportens undertitel." +msgstr "Stilen der benyttes til Sosa-nummeret for faderens linje." #: AncestryTableReport/AncestryTableReport.py:552 -#, fuzzy -#| msgid "The style used for the subtitle of the report." msgid "The style used for the Sosa number of the maternal branch." -msgstr "Stilen der benyttes til rapportens undertitel." +msgstr "Stilen der benyttes til Sosa-nummeret for moderens linje." #: AncestryTableReport/AncestryTableReport.py:562 -#, fuzzy -#| msgid "The style used for the title of the page." msgid "The style used for the data of the males." -msgstr "Fontformat for sidens titel" +msgstr "Stilen der benyttes for mændenes data." #: AncestryTableReport/AncestryTableReport.py:572 -#, fuzzy -#| msgid "The style used for the title of the page." msgid "The style used for the data of the females." -msgstr "Fontformat for sidens titel" +msgstr "Stilen de benyttes for kvindernes data." #: AncestryTableReport/AncestryTableReport.py:583 -#, fuzzy -#| msgid "The style used for names." msgid "The style used for the marriage." -msgstr "Stilen der benyttes til navne." +msgstr "Stilen der benyttes for ægteskabet." #: AncestryTableReport/AncestryTableReport.py:593 msgid "" "The style used for an empty row.\n" "To enlarge the height of the empty row, just increse the size of the police." msgstr "" +"Stilen der benyttes for en tom rækker.\n" +"For at forøge højden på den tomme række, øg simpelthen størrelsen på tegnene." #: AncestryTableReport/AncestryTableReport.py:632 -#, fuzzy -#| msgid "The style used for the header in the Index of Places." msgid "" "The style used for the header table of the number of ancestors per " "generation." -msgstr "Stilen der benyttes til oveskiften i Indeks over Steder." +msgstr "" +"Stilen der benyttes til overskiften i tabellen over antallet af aner per " +"generation." #: AncestryTableReport/AncestryTableReport.py:643 #, fuzzy #| msgid "The style used for the table of contents header." msgid "The style used for the number of ancestors per generation." -msgstr "Stilen der bruges til indholdsfortegnelsens overskrift." +msgstr "Stilen der bruges til antallet af aner per generation." #: AncestryTableReport/AncestryTableReport.py:654 -#, fuzzy -#| msgid "The style used for the title of the report." msgid "" "The style used for the total line of the number of ancestors per generation." -msgstr "Stilen der benyttes til rapportens titel." +msgstr "Stilen der benyttes til totallinjen af antallet af aner per generation." #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:24 #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:32 -#, fuzzy -#| msgid "Inverse" msgid "Anniversaries" -msgstr "Omvendt" +msgstr "Årsdage" #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:25 -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "A gramplet that displays the anniversaries of events" -msgstr "en gramplet der viser fødselsdage for levende persone" +msgstr "En gramplet der viser årsdage for begivenheder" #: AnniversariesGramplet/AnniversariesGramplet.py:52 #, fuzzy @@ -16142,7 +16117,7 @@ msgstr "" #: GrampsWebSync/grampswebsync.py:923 msgid "Merge" -msgstr "" +msgstr "Sammenføj" #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" @@ -20814,7 +20789,7 @@ msgstr "Visning for sted koordinat gramplet." #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:43 msgid "Place and Coordinates" -msgstr "Sted og koorsinater" +msgstr "Sted og koordinater" #: PlaceCoordinateGramplet/PlaceCoordinateGramplet.gpr.py:44 msgid "Gramplet that simplifies setting the coordinates of a place" From a13028c65f12e7d57bb51e96f734fb357d0ab934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20M=C3=A4kel=C3=A4inen?= Date: Wed, 5 Aug 2026 20:02:10 +0200 Subject: [PATCH 103/156] Translated using Weblate (Finnish) Currently translated at 63.0% (3498 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/fi/ Translated using Weblate (Finnish) Currently translated at 61.1% (3395 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/fi/ Translated using Weblate (Finnish) Currently translated at 60.7% (3372 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/fi/ --- po/fi.po | 490 +++++++++++++++++++++---------------------------------- 1 file changed, 189 insertions(+), 301 deletions(-) diff --git a/po/fi.po b/po/fi.po index 2c54a4753..dc4910586 100644 --- a/po/fi.po +++ b/po/fi.po @@ -25,8 +25,8 @@ msgstr "" "Project-Id-Version: fi\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-02 18:04+0000\n" -"Last-Translator: Matti Niemelä \n" +"PO-Revision-Date: 2026-07-26 17:40+0000\n" +"Last-Translator: Juha Mäkeläinen \n" "Language-Team: Finnish \n" "Language: fi\n" @@ -34,7 +34,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.6\n" +"X-Generator: Weblate 2026.8.dev0\n" "Generated-By: pygettext.py 1.4\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 @@ -157,33 +157,28 @@ msgid "Ancestry Table" msgstr "Sukutaulu" #: AncestryTableReport/AncestryTableReport.gpr.py:4 -#, fuzzy -#| msgid "Produces descendants lines of a person" msgid "Produces a table of ancestry about a person" -msgstr "Tuottaa henkilön jälkeläislinjoja" +msgstr "Listaa henkilön esi-isät" #: AncestryTableReport/AncestryTableReport.py:133 #, python-format msgid "Person %(name)s is not in the Database" -msgstr "" +msgstr "Henkilöä %(name)s ei ole tietokannassa" #: AncestryTableReport/AncestryTableReport.py:277 -#, fuzzy, python-format -#| msgid "The style used for names" +#, python-format msgid "Ancestry Table Report for %(name)s" -msgstr "Nimien tyyli" +msgstr "Henkilön %(name)s esi-isäraportti" #: AncestryTableReport/AncestryTableReport.py:311 -#, fuzzy, python-format -#| msgid "Generations up" +#, python-format msgid "Generation %(gen_number)d" -msgstr "Jälkipolvet" +msgstr "Sukupolvi %(sukupolvien_numero)d" #: AncestryTableReport/AncestryTableReport.py:360 -#, fuzzy, python-format -#| msgid "Number of Ancestors found " +#, python-format msgid "Number of Ancestors for %(name)s" -msgstr "Löydettyjen esi-isien lukumäärä " +msgstr "Henkilön %(name)s löydettyjen esi-isien määrä" #: AncestryTableReport/AncestryTableReport.py:370 #: NumberOfAncestorsQuickview/NumberOfAncestorsQuickview.py:54 @@ -194,31 +189,27 @@ msgstr "Sukupolvi" #: AncestryTableReport/AncestryTableReport.py:472 msgid "To start a new page after each generation." -msgstr "" +msgstr "Aloita uusi sivu jokaisen sukupolven jälkeen." #: AncestryTableReport/AncestryTableReport.py:475 -#, fuzzy -#| msgid "Number of ancestors" msgid "Number of ancestors per generation" -msgstr "Esivanhempien lukumäärä" +msgstr "Esivanhempien lukumäärä sukupolvea kohden" #: AncestryTableReport/AncestryTableReport.py:476 -#, fuzzy -#| msgid "Whether to display the theoretical number of ancestor by generation" msgid "Add a page with tne number of ancestors per generation." -msgstr "Näytetäänkö esi-isien teoreettinen lukumäärä sukupolvittain" +msgstr "Lisää sivu, jossa on esi-isien lukumäärä sukupolvea kohden." #: AncestryTableReport/AncestryTableReport.py:495 -#, fuzzy -#| msgid "Color the name to indicate a person's gender in the chart." msgid "Mask the name of the calendar in the dates" -msgstr "Väritä nimi ilmaisemaan henkilön sukupuoli kaaviossa." +msgstr "Älä näytä kalenterin nimeä päivämäärien yhteydessä" #: AncestryTableReport/AncestryTableReport.py:496 msgid "" "By default, except for gregorian dates, Gramps shows the name of the " "calendar in the dates." msgstr "" +"Oletusarvoisesti Gramps näyttää kalenterin nimen päivämäärissä, lukuun " +"ottamatta gregoriaanisia päivämääriä." #: AncestryTableReport/AncestryTableReport.py:519 #: RepositoriesReport/RepositoriesReport.py:175 @@ -228,98 +219,76 @@ msgid "The style used for the title of the report." msgstr "Raportin otsikon tyyli." #: AncestryTableReport/AncestryTableReport.py:541 -#, fuzzy -#| msgid "The style used for the subtitle of the report." msgid "The style used for the Sosa number of the paternal branch." -msgstr "Raportin alaotsikon tyyli." +msgstr "Isänhaaran Sosa-numerossa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:552 -#, fuzzy -#| msgid "The style used for the subtitle of the report." msgid "The style used for the Sosa number of the maternal branch." -msgstr "Raportin alaotsikon tyyli." +msgstr "Äitihaaran Sosa-numerossa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:562 -#, fuzzy -#| msgid "The style used for the title of the page." msgid "The style used for the data of the males." -msgstr "Sivun otsikossa käytetty tyyli." +msgstr "Miesten tiedossa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:572 -#, fuzzy -#| msgid "The style used for the title of the page." msgid "The style used for the data of the females." -msgstr "Sivun otsikossa käytetty tyyli." +msgstr "Naisten tiedoissa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:583 -#, fuzzy -#| msgid "The style used for names." msgid "The style used for the marriage." -msgstr "Nimissä käytetty tyyli." +msgstr "Avioliitossa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:593 msgid "" "The style used for an empty row.\n" "To enlarge the height of the empty row, just increse the size of the police." msgstr "" +"Tyhjän rivin tyyli.\n" +"Suurenna korkeutta lisäämällä fonttikokoa." #: AncestryTableReport/AncestryTableReport.py:632 -#, fuzzy -#| msgid "The style used for the header in the Index of Places." msgid "" "The style used for the header table of the number of ancestors per " "generation." -msgstr "Paikkahakemiston otsikossa käytetty tyyli." +msgstr "Sukupolven esi-isien lukumäärän otsikossa käytetty tyyli." #: AncestryTableReport/AncestryTableReport.py:643 -#, fuzzy -#| msgid "The style used for the table of contents header." msgid "The style used for the number of ancestors per generation." -msgstr "Sisällysluettelon otsikon tyyli." +msgstr "Sukupolven esi-isien lukuäärän tyyli." #: AncestryTableReport/AncestryTableReport.py:654 -#, fuzzy -#| msgid "The style used for the title of the report." msgid "" "The style used for the total line of the number of ancestors per generation." -msgstr "Raportin otsikon tyyli." +msgstr "Sukupolven esi-isien määrän tyyli." #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:24 #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:32 -#, fuzzy -#| msgid "Inverse" msgid "Anniversaries" -msgstr "Käänteinen" +msgstr "Merkkipäivät" #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:25 -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "A gramplet that displays the anniversaries of events" -msgstr "Gramplet, joka näyttää elävien ihmisten syntymä päivät ja iät" +msgstr "Gramplet, joka näyttää tapahtumien vuosipäivät" #: AnniversariesGramplet/AnniversariesGramplet.py:52 -#, fuzzy -#| msgid "Double-click on a row to edit the selected participant." msgid "Double-click on a row to edit the event." -msgstr "Voit muokata osallistujaa kaksoisnapsauttamalla sen riviä." +msgstr "Muokkaa tapahtumaa kaksoisnapsauttamalla riviä." #: AnniversariesGramplet/AnniversariesGramplet.py:59 -#, fuzzy -#| msgid "Other participants" msgid "Participant" -msgstr "Muut osallistujat" +msgstr "Osallistuja" #: ArchiveAssist/ArchiveAssist.gpr.py:22 msgid "" "Parses strings from Riksarkivet and ArkivDigital to create sources and " "citations." msgstr "" +"Jäsentää Riksarkivet ja ArkivDigital -merkkijonoja lähteiden ja viittausten " +"luomiseksi." #: ArchiveAssist/ArchiveAssist.gpr.py:26 -#, fuzzy -#| msgid "Archive file" msgid "Archive Assist" -msgstr "Arkistotiedosto" +msgstr "Arkistoavustaja" #: AssociationsTool/associationstool.gpr.py:36 msgid "Check Associations data" @@ -1027,7 +996,7 @@ msgstr "valmis!\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:432 #, python-format msgid "Skipped %d people due to errors (see log).\n" -msgstr "" +msgstr "%d henkilöä ohitettiin virheiden vuoksi (katso loki).\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:347 msgid "" @@ -1102,7 +1071,7 @@ msgstr "%d tapahtumaa lisätty." #: CalculateEstimatedDates/CalculateEstimatedDates.py:562 #, python-format msgid " (Skipped %d rows due to errors; see log.)" -msgstr "" +msgstr " (%d riviä ohitettu virheiden vuoksi; katso loki.)" #: CalculateEstimatedDates/CalculateEstimatedDates.py:589 msgid "Estimated date" @@ -1173,17 +1142,11 @@ msgid "Send" msgstr "Lähetä" #: ChatWithTree/ChatWithTree.py:163 -#, fuzzy -#| msgid "Chat with Tree initialized. Type /help for help." msgid "Chat with Tree initialized. Type /help for help." msgstr "" "Keskustelu sukupuun kanssa on aloitettu. Kirjoita /help saadaksesi apua." #: ChatWithTree/ChatWithTree.py:463 -#, fuzzy -#| msgid "" -#| "The ChatWithTree addon is not yet initialized. Please reload Gramps or " -#| "select a database." msgid "" "The ChatWithTree addon is not yet initialized. Please " "reload Gramps or select a database." @@ -1193,11 +1156,11 @@ msgstr "" #: ChatWithTree/ChatWithTree.py:472 msgid "The chatbot is currently processing a query. Please wait." -msgstr "" +msgstr "Chatbot käsittelee parhaillaan kyselyä. Odota." #: ChatWithTree/ChatWithTree.py:499 msgid "An error occurred while processing your query." -msgstr "" +msgstr "Virhe kyselysi käsittelyssä." #: ChatWithTree/chatwithllm.py:119 msgid "Tree: '{}'" @@ -1511,8 +1474,7 @@ msgstr "" "tiivistettävänä puuna käyttäen D3.js JavaScript-kirjastoa." #: D3Charts/DescendantIndentedTree.py:678 -#, fuzzy, python-format -#| msgid "See %(reference)s : %(spouse)s" +#, python-format msgctxt "spouse" msgid "See %(reference)s : %(spouse)s" msgstr "Katso %(reference)s : %(spouse)s" @@ -2083,14 +2045,12 @@ msgstr "paikassa" #: DataEntryGramplet/DataEntryGramplet.py:428 #: DataEntryGramplet/DataEntryGramplet.py:507 -#, fuzzy -#| msgid "Family Tree file" msgid "No Family Tree is open." -msgstr "Sukupuutiedosto" +msgstr "Sukupuuta ei ole avattu." #: DataEntryGramplet/DataEntryGramplet.py:429 msgid "Please open a Family Tree to edit data." -msgstr "" +msgstr "Avaa sukupuu muokataksesi tietoja." #: DataEntryGramplet/DataEntryGramplet.py:446 #: DataEntryGramplet/DataEntryGramplet.py:578 @@ -2100,7 +2060,7 @@ msgstr "Gramplet-tietojen muokkaus: %s" #: DataEntryGramplet/DataEntryGramplet.py:508 msgid "Please open a Family Tree before adding a person." -msgstr "" +msgstr "Avaa sukupuu lisätäksesi henkilön." #: DataEntryGramplet/DataEntryGramplet.py:524 msgid "Can't add new person." @@ -2260,54 +2220,40 @@ msgid "Deep Connections" msgstr "Syväsuhteet" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:106 -#, fuzzy -#| msgid "Pause" msgid "⏸ Pause" -msgstr "Pysäytä" +msgstr "⏸ Pysäytä" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:108 msgid "Pause the current search" -msgstr "" +msgstr "Keskeytä nykyinen haku" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:111 msgid "▶ Continue" -msgstr "" +msgstr "▶ Jatka" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:114 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Continue searching for more relations" -msgstr "" -"Pysäytetty.\n" -"Etsi lisää suhteita painamalla Jatka.\n" +msgstr "Jatka hakua löytääksesi lisää suhteita" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:118 -#, fuzzy -#| msgid "Copy" msgid "📋 Copy" -msgstr "Kopioi" +msgstr "📋 Kopioi" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:120 -#, fuzzy -#| msgid "Copy link to clipboard" msgid "Copy selected people to clipboard" -msgstr "Kopioi linkki leikepöydälle" +msgstr "Kopioi valitut henkilöt leikepöydälle" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:128 msgid "🗑 Clear" -msgstr "" +msgstr "🗑 Tyhjennä" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:129 msgid "Clear all results and reset" -msgstr "" +msgstr "Tyhjennä kaikki tulokset ja palauta alkuarvoihin" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:158 -#, fuzzy -#| msgid "Start type to search" msgid "Ready to search" -msgstr "Ala kirjoittaa hakusanaa" +msgstr "Valmis etsintään" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:185 #, python-brace-format @@ -2315,6 +2261,8 @@ msgid "" "Search Depth: {depth} | People Processed: {processed} | Queue Size: " "{queue_size}" msgstr "" +"Haun syvyys: {depth} | Henkilötä käsitelty: {processed} | Jonon koko: " +"{queue_size}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:216 msgid "mentioned in note" @@ -2351,24 +2299,20 @@ msgstr "" " %s henkilölle " #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:336 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Home Person set" -msgstr "Aktiivihenkilöä ei ole asetettu." +msgstr "Kotihenkilöä ei ole asetettu." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:341 msgid "No Active Person set." msgstr "Aktiivihenkilöä ei ole asetettu." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:342 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Active Person set" -msgstr "Aktiivihenkilöä ei ole asetettu." +msgstr "Virhe: aktiivista henkilöä ei ole asetettu" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:347 msgid "Initializing search..." -msgstr "" +msgstr "Hakua alustetaan..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:366 msgid "Looking for relationship between\n" @@ -2385,10 +2329,8 @@ msgid " %s (Active Person)...\n" msgstr " %s (aktiivihenkilö)...\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:382 -#, fuzzy -#| msgid "Fetching records..." msgid "Searching for connections..." -msgstr "Noudetaan tietueita…" +msgstr "Yhteyksiä etsitään..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:402 #, python-format @@ -2408,50 +2350,33 @@ msgstr "" "Etsi lisää suhteita painamalla Jatka.\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:427 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Paused - Press Continue to search for more relations" -msgstr "" -"Pysäytetty.\n" -"Etsi lisää suhteita painamalla Jatka.\n" +msgstr "Keskeytetty - Paina Jatka etsiäksesi lisää suhteita" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:456 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." +#, python-format msgid "" "\n" "Search completed. %d relation paths found." msgstr "" "\n" -"Haku valmis. %d suhdetta löytynyt." +"Haku suoritettu. Löydettiin %d suhdepolkua." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:460 -#, fuzzy -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." msgid "Search completed - {} relation paths found" -msgstr "" -"\n" -"Haku valmis. %d suhdetta löytynyt." +msgstr "Haku valmis - %d suhdepolkua löytynyt" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:471 msgid "Error during search: {}" -msgstr "" +msgstr "Virhe haun aikana: {}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:481 -#, fuzzy -#| msgid "Removing '%s'..." msgid "Resuming search..." -msgstr "Poistetaan '%s'..." +msgstr "Hakua jatketaan..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:489 msgid "Search interrupted by user" -msgstr "" +msgstr "Käyttäjä keskeytti haun" #: DenominoViso/DenominoViso.gpr.py:9 msgid "DenominoViso" @@ -3103,7 +3028,7 @@ msgstr "Paikkahakemisto" #: DescendantBooks/DetailedDescendantBookReport.py:544 msgid "Index of Dates" -msgstr "Päivämäärähakemisto" +msgstr "Päiväyshakemisto" #: DescendantBooks/DetailedDescendantBookReport.py:567 msgid "Index of Names" @@ -3694,8 +3619,6 @@ msgstr "" "0 = ei rajoitusta " #: DescendantsLines/DescendantsLines.py:1618 -#, fuzzy -#| msgid "Font name" msgid "Font Name" msgstr "Fontin nimi" @@ -3704,12 +3627,12 @@ msgid "" "Name of the Font to use. On Windows enter the file name of the .ttf at /" "Windows/Fonts" msgstr "" +"Käytettävän fontin nimi. Windowsissa kirjoita .ttf-tiedoston nimi " +"osoitteesssa /Windows/Fonts" #: DescendantsLines/DescendantsLines.py:1623 -#, fuzzy -#| msgid "Database size" msgid "Font base size" -msgstr "Tietokannan koko" +msgstr "Fontin peruskoko" #: DescendantsLines/DescendantsLines.py:1629 msgid "Box around Person's block" @@ -5704,7 +5627,7 @@ msgstr "" #: FamilySheet/FamilySheet.py:232 FamilySheet/FamilySheet.py:275 #, python-format msgid "→ %s" -msgstr "" +msgstr "→ %s" #: FamilySheet/FamilySheet.py:397 #, python-format @@ -6291,17 +6214,15 @@ msgstr "" #: FilterRules/matchpersonfilterrole.gpr.py:7 msgid "Events from people with role" -msgstr "" +msgstr "Tapahtumia henkilöiltä, joilla on rooli" #: FilterRules/matchpersonfilterrole.gpr.py:8 -#, fuzzy -#| msgid "Matches people with an event with a selected role" msgid "Matches event of people filter with role" -msgstr "Poimii henkilöt, joiden tapahtumissa on valittu rooli" +msgstr "Poimii henkilöt, joiden tapahtumissa on tämä rooli" #: FilterRules/matchpersonfilterrole.py:69 msgid "Role:" -msgstr "" +msgstr "Rooli:" #: FilterRules/multipleparents.gpr.py:27 FilterRules/multipleparents.gpr.py:28 msgid "Multiple Parents Filter" @@ -6451,33 +6372,33 @@ msgid "Fix coords" msgstr "Korjaa koordinaatit" #: Form/CensusCheckQuickview.gpr.py:9 -#, fuzzy -#| msgid "Censuses" msgid "CensusCheck" -msgstr "Väestönlaskennat" +msgstr "Väestönlaskennan valvonta" #: Form/CensusCheckQuickview.gpr.py:10 msgid "" "Check whether any Census events are missing for a person and some of their " "descendents" msgstr "" +"Tarkista, puuttuuko henkilön ja joidenkin hänen jälkeläistensä " +"väestönlaskennan tapahtumia" #: Form/CensusCheckQuickview.gpr.py:23 -#, fuzzy -#| msgid "Censuses" msgid "CensusCheckUp" -msgstr "Väestönlaskennat" +msgstr "Väestönlaskennan tarkastus" #: Form/CensusCheckQuickview.gpr.py:24 msgid "" "Check whether any Census events are missing for a person and some of their " "ancestors" msgstr "" +"Tarkista, puuttuuko henkilön ja joidenkin hänen esivanhempiensa " +"väestönlaskennan tapahtumia" #: Form/CensusCheckQuickview.py:122 Form/CensusCheckUpQuickview.py:122 #, python-format msgid "Census Check for %s" -msgstr "" +msgstr "Väestönlaskennan valvonta henkilölle %s" #: Form/editform.py:192 #, python-format @@ -6500,7 +6421,7 @@ msgstr "Viite:" #: Form/editform.py:393 msgid "[Source recreated after deletion mid-form-edit]" -msgstr "" +msgstr "[Poistettu lähde luotu uudelleen lomaketta muokattaessa]" #: Form/editform.py:446 msgid "Headings" @@ -6512,19 +6433,15 @@ msgstr "Lomake" #: Form/form.py:174 msgid "XML syntax error in Form definition file" -msgstr "" +msgstr "XML-syntaksivirhe lomakkeen kuvaustiedostossa" #: Form/form.py:185 -#, fuzzy -#| msgid "Failed to read proto file %s: %s" msgid "Failed to read Form definition file" -msgstr "Mallitiedoston %s lukeminen epäonnistui: %s" +msgstr "Lomakkeen määritystiedoston lukeminen epäonnistui" #: Form/form.py:194 -#, fuzzy -#| msgid "Invalid Destination Directory" msgid "Invalid Form definition file" -msgstr "Virheellinen kohdehakemisto" +msgstr "Virheellinen lomakkeen määritystiedosto" #: Form/form_ca.xml.h:1 Form/form_ca.xml.h:92 Form/form_ca.xml.h:210 #: Form/form_ca.xml.h:288 Form/form_ca.xml.h:342 Form/form_ca.xml.h:390 @@ -9955,13 +9872,11 @@ msgstr "" #, fuzzy #| msgid "Enumerator" msgid "Signature of Enumerator" -msgstr "Luetelma" +msgstr "Laskijan allekirjoitus" #: Form/form_ie.xml.h:8 -#, fuzzy -#| msgid "City or Borough" msgid "County or Co. Borough" -msgstr "Kaupunki tai kaupunginosa" +msgstr "Maakunta tai kaupunginosa" #: Form/form_ie.xml.h:9 msgid "District Electoral Division or Ward" @@ -9987,21 +9902,16 @@ msgstr "" #: Form/form_ie.xml.h:16 Form/form_ie.xml.h:113 #, fuzzy -#| msgid "Maiden Surname" msgid "Name and Surname" msgstr "Tyttönimi" #: Form/form_ie.xml.h:18 -#, fuzzy -#| msgid "10. Relationship to Head of Family" msgid "Relationship to Head of Household" -msgstr "10. Suhde perheenpäähän" +msgstr "Suhde perheenpäähän" #: Form/form_ie.xml.h:19 -#, fuzzy -#| msgid "Default Age" msgid "Year Age" -msgstr "Oletusikä" +msgstr "Ikävuosi" #: Form/form_ie.xml.h:20 Form/form_us.xml.h:2561 msgid "Age in years" @@ -10009,18 +9919,17 @@ msgstr "" #: Form/form_ie.xml.h:21 msgid "Month Age" -msgstr "" +msgstr "Ikäkuukausi" # Yhdistä tuontiin #: Form/form_ie.xml.h:22 -#, fuzzy -#| msgid "Merge into import" msgid "Age in months" -msgstr "Yhdistetään tuonnista" +msgstr "Ikä kuukausina" #: Form/form_ie.xml.h:25 +#, fuzzy msgid "Marriage or Orphanhood" -msgstr "" +msgstr "Avioliitto tai orpous" #: Form/form_ie.xml.h:28 msgid "" @@ -10040,7 +9949,7 @@ msgstr "Avioliiton solmimispäivä" #: Form/form_ie.xml.h:35 msgid "Months married in present Marriage" -msgstr "" +msgstr "Kuukaudet naimisissa nykyisessä avioliitossa" #: Form/form_ie.xml.h:36 #, fuzzy @@ -15495,6 +15404,9 @@ msgid "" "generations per page. Useful for large sandclock trees that otherwise clip " "off the rendered page." msgstr "" +"Käytä genealogytree-mallia 'database pole reduced', joka pakkaa enemmän " +"sukupolvia sivua kohden. Hyödyllinen suurille tiimalasikaavioille, jotka ei " +"muuten mahdu tulossivulle." #: GenealogyTree/treeplugins.gpr.py:31 msgid "Ancestor tree using LaTeX genealogytree" @@ -15712,8 +15624,7 @@ msgid "GOV error on id %s with code: " msgstr "GOV-virhe tunnuksella %s, koodi: " #: GetGOV/getgov.py:555 -#, fuzzy, python-format -#| msgid "from %s to %s" +#, python-format msgctxt "end" msgid "from %(begin)s to %(end)s" msgstr "välillä %s – %s" @@ -15777,14 +15688,12 @@ msgstr "" #: GrampsAssistant/grampsassistant.gpr.py:26 #: GrampsAssistant/grampsassistant.py:1417 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant" -msgstr "Gramps versio" +msgstr "Gramps-avustaja" #: GrampsAssistant/grampsassistant.gpr.py:27 msgid "AI assistant for querying your Gramps family tree" -msgstr "" +msgstr "Tekoälyavustaja Gramps-sukupuun kyselyihin" #: GrampsAssistant/grampsassistant.py:58 msgid "" @@ -15794,29 +15703,32 @@ msgid "" "provided tools — never write code, simulate results, or make up data. If no " "tool exists for the requested information, say so plainly." msgstr "" +"Olet avulias sukututkimusavustaja, jolla on pääsy käyttäjän Gramps-" +"tietokantaan. Vastaa kysymyksiin ihmisistä, perheistä, tapahtumista ja " +"suhteista. Kun tarvitset tietoa tietokannasta, käytä annettuja työkaluja – " +"älä koskaan kirjoita koodia, simuloi tuloksia tai keksi tietoja. Jos " +"pyydetylle tiedolle ei ole työkalua, sano se suoraan." #: GrampsAssistant/grampsassistant.py:209 -#, fuzzy -#| msgid "Extra style settings:" msgid "Gramps Assistant settings" -msgstr "Muita tyyliasetuksia:" +msgstr "Gramps-avustajan asetukset" #: GrampsAssistant/grampsassistant.py:213 msgid "Clear conversation and context" -msgstr "" +msgstr "Tyhjennä keskustelu ja konteksti" #: GrampsAssistant/grampsassistant.py:388 #: GrampsAssistant/grampsassistant.py:749 -#, fuzzy -#| msgid "Gramps version" msgid "Gramps Assistant:" -msgstr "Gramps versio" +msgstr "Gramps-avustaja:" #: GrampsAssistant/grampsassistant.py:390 msgid "" "Ask me anything about the Gramps program or your specific Gramps family " "tree. Use the ⚙ button to configure the AI.\n" msgstr "" +"Kysy minulta mitä tahansa Gramps-ohjelmasta tai omasta Gramps-sukupuustasi. " +"Käytä ⚙-painiketta tekoälyn asetusten muuttamiseen.\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15824,10 +15736,13 @@ msgid "" "No model configured. Please click the Settings button to choose a backend " "and model before chatting.\n" msgstr "" +"\n" +"Mallia ei ole määritetty. Valitse taustajärjestelmä ja malli napsauttamalla " +"Asetukset-painiketta ennen keskustelun aloittamista.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." -msgstr "" +msgstr "Ajattelu..." #: GrampsAssistant/grampsassistant.py:824 #, python-brace-format @@ -15836,32 +15751,38 @@ msgid "" "it before launching Gramps:\n" " export {var}=your-key-here" msgstr "" +"API-avainvirhe: ympäristömuuttujaa {var} ei ole asetettu tai se on " +"virheellinen. Aseta se ennen Grampsin käynnistämistä:\n" +"export {var}=your-key-here" #: GrampsAssistant/grampsassistant.py:830 msgid "" "API key error: this provider requires an API key. Open Settings and enter " "the environment variable name for your API key (e.g. OPENAI_API_KEY)." msgstr "" +"API-avainvirhe: tämä palvelu vaatii API-avaimen. Avaa Asetukset ja anna API-" +"avaimesi ympäristömuuttujan nimi (esim. OPENAI_API_KEY)." #: GrampsAssistant/grampsassistant.py:973 -#, fuzzy -#| msgid "Done!\n" msgid "Done.\n" -msgstr "Valmis!\n" +msgstr "Valmis.\n" #: GrampsAssistant/grampsassistant.py:1203 msgid "System Prompt:" -msgstr "" +msgstr "Järjestelmäkehote:" #: GrampsAssistant/grampsassistant.py:1217 msgid "Simplify tools (recommended for smaller/local models)" msgstr "" +"Yksinkertaista työkaluja (suositellaan pienemmille/paikallisille malleille)" #: GrampsAssistant/grampsassistant.py:1221 msgid "" "When enabled, only the tools relevant to your question are sent to the " "model. This improves performance with smaller local models." msgstr "" +"Kun tämä on käytössä, malliin lähetetään vain kysymykseesi liittyvät " +"työkalut. Tämä parantaa suorituskykyä pienempien paikallisten mallien kanssa." #: GrampsAssistant/grampsassistant.py:1230 #, fuzzy @@ -15874,65 +15795,63 @@ msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" +"Paikallisen OpenAI-yhteensopivan palvelimen URL-osoite. Ollama: http://" +"localhost:11434 LM Studio: http://localhost:1234 llama.cpp: http://" +"localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" -msgstr "" +msgstr "mallinimi (jätä tyhjäksi, jos tiedosto on LM Studio / llama.cpp)" #: GrampsAssistant/grampsassistant.py:1260 msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" +"Paikallispalvelimelta pyydettävä malli. Pakollinen Ollamalle (esim. llama3.1)" +". Jätä tyhjäksi LM Studiolle tai llama.cpp-tiedostolle, jotka käyttävät " +"ladattua mallia." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 -#, fuzzy -#| msgid "Modern" msgid "Model:" -msgstr "Moderni" +msgstr "Malli:" #: GrampsAssistant/grampsassistant.py:1273 -#, fuzzy -#| msgid "Foundation date:" msgid "Use Foundational Model" -msgstr "Perustamispäivä:" +msgstr "Käytä perusmallia" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" -msgstr "" +msgstr "esim. OPENAI_API_KEY" #: GrampsAssistant/grampsassistant.py:1317 msgid "Name of the environment variable holding your API key." -msgstr "" +msgstr "API-avaimesi sisältävän ympäristömuuttujan nimi." #: GrampsAssistant/grampsassistant.py:1319 msgid "API key env var:" -msgstr "" +msgstr "API-avaimen ympäristömuuttuja:" #: GrampsAssistant/grampsassistant.py:1322 msgid "Backend:" -msgstr "" +msgstr "Taustajärjestelmä:" #: GrampsAssistant/grampsassistant.py:1334 -#, fuzzy -#| msgid "Website URL" msgid "Base URL:" -msgstr "Nettisivuston URL" +msgstr "Nettisivuston URL:" #: GrampsAssistant/grampsassistant.py:1338 -#, fuzzy -#| msgid "Spouse name:" msgid "Model name:" -msgstr "Puolison nimi:" +msgstr "Mallin nimi:" #: GrampsChat/GrampsChat.gpr.py:4 GrampsChat/GrampsChat.gpr.py:9 msgid "GrampsChat" -msgstr "" +msgstr "GrampsChat" #: GrampsChat/GrampsChat.gpr.py:5 msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" -msgstr "" +msgstr "AI Chatbot Gramplet (vaatii yhteyden LLM-palveluun)" #: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 #: GrampsWebSync/grampswebsync.py:212 @@ -15961,44 +15880,36 @@ msgid "Media files are in sync." msgstr "Mediatiedostoissa ei synkronoitavaa." #: GrampsWebSync/grampswebsync.py:345 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media file." -#| msgid_plural "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media files." -msgstr "%s mediatiedosto tuotiin onnistuneesti." +msgstr "%s mediatiedostoa tuotiin onnistuneesti." #: GrampsWebSync/grampswebsync.py:348 -#, fuzzy, python-format -#| msgid "Encountered %s error during download." -#| msgid_plural "Encountered %s errors during download." +#, python-format msgid "Encountered %s errors during download." -msgstr "Latauksessa ilmeni %s virhe." +msgstr "Tuonnin aikana ilmeni virhe %s." #: GrampsWebSync/grampswebsync.py:354 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media file." -#| msgid_plural "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media files." -msgstr "%s mediatiedosto ladattiin onnistuneesti." +msgstr "%s mediatiedostoa lähetettiin onnistuneesti." #: GrampsWebSync/grampswebsync.py:357 -#, fuzzy, python-format -#| msgid "Encountered %s error during upload." -#| msgid_plural "Encountered %s errors during upload." +#, python-format msgid "Encountered %s errors during upload." -msgstr "Latauksen aikana ilmeni %s virhe." +msgstr "Lähetyksen aikana ilmeni virhe %s." #: GrampsWebSync/grampswebsync.py:375 msgid "Authentication failed. Please check your username and password." -msgstr "Todennus epäonnistui. Tarkista käyttäjänimesi ja salasanasi." +msgstr "Todennus epäonnistui. Tarkista käyttäjätunnuksesi ja salasanasi." #: GrampsWebSync/grampswebsync.py:379 msgid "Access forbidden. Please check username and password." -msgstr "Pääsy kielletty. Tarkista käyttäjätunnus ja salasana." +msgstr "Pääsy evätty. Tarkista käyttäjätunnus ja salasana." #: GrampsWebSync/grampswebsync.py:383 msgid "GrampsWeb service not found. Please check the URL." -msgstr "GrampsWeb-palvelua ei löytynyt. Tarkista URL." +msgstr "GrampsWeb-palvelua ei löytynyt. Tarkista URL-osoite." #: GrampsWebSync/grampswebsync.py:387 msgid "Too many requests, please try again in a few seconds." @@ -16184,89 +16095,70 @@ msgid "Missing remotely" msgstr "Puuttuu etätiedoista" #: GrampsWebSync/grampswebsync.py:1130 -#, fuzzy, python-format -#| msgid "Downloading %s media file" -#| msgid_plural "Downloading %s media files" +#, python-format msgid "Downloading %s media file(s)" -msgstr "Tuodaan Grampsista %s mediatiedosto" +msgstr "Tuodaan Grampsista %s mediatiedostoa" #: GrampsWebSync/grampswebsync.py:1138 -#, fuzzy, python-format -#| msgid "Uploading %s media file" -#| msgid_plural "Uploading %s media files" +#, python-format msgid "Uploading %s media file(s)" -msgstr "Viedään Grampsiin %s mediatiedosto" +msgstr "Viedään Grampsiin %s mediatiedostoa" #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" -msgstr "" +msgstr "Gram.py-scripti" #: GrampyScript/GrampyScript.gpr.py:24 msgid "Run a special Gramps Python script" -msgstr "" +msgstr "Suorita erityinen Gramps Python -skripti" #: GrampyScript/GrampyScript.py:91 -#, fuzzy -#| msgid "Loaded from file: %s" msgid "Load query from a .script file" -msgstr "Ladattu tiedostosta: %s" +msgstr "Komento .script-tiedostosta" #: GrampyScript/GrampyScript.py:121 -#, fuzzy -#| msgid "Save tree as file" msgid "Save query to a .script file" -msgstr "Tallenna puu tiedostoon" +msgstr "Tallenna kysely .script-tiedostoon" #: GrampyScript/GrampyScript.py:141 -#, fuzzy -#| msgid "Downloading %s media file" -#| msgid_plural "Downloading %s media files" msgid "Download results as a CSV file" -msgstr "Tuodaan Grampsista %s mediatiedosto" +msgstr "Lataa tulokset CSV-tiedostona" #: GrampyScript/GrampyScript.py:303 -#, fuzzy -#| msgid "OpenAI" msgid "Open..." -msgstr "OpenAI" +msgstr "Avaa..." #: GrampyScript/GrampyScript.py:305 -#, fuzzy -#| msgid "Save images in ..." msgid "Save as..." -msgstr "Tallenna kuvat kansioon..." +msgstr "Tallenna nimellä..." #: GrampyScript/GrampyScript.py:319 msgid "Save as CSV" -msgstr "" +msgstr "Tallenna CVS-muodossa" #: GrampyScript/GrampyScript.py:320 -#, fuzzy -#| msgid "Copy link to clipboard" msgid "Copy to clipboard" -msgstr "Kopioi linkki leikepöydälle" +msgstr "Kopioi leikepöydälle" #: GrampyScript/GrampyScript.py:395 -#, fuzzy -#| msgid "Quilt Chart" msgid "Chart" -msgstr "Peittokaavio" +msgstr "Kaavio" #: GrampyScript/GrampyScript.py:400 msgid "Execute " -msgstr "" +msgstr "Suorita " #: GrampyScript/GrampyScript.py:402 msgid "Execute the script" -msgstr "" +msgstr "Suorita scripti" #: GrampyScript/GrampyScript.py:416 msgid "Ready..." -msgstr "" +msgstr "Valmis...." #: GrampyScript/GrampyScript.py:1028 msgid "Gram.py Script Edited Data" -msgstr "" +msgstr "Gram.py-skriptin muokkaamat tiedot" #: GraphView/avatars.py:60 msgid "Dark (default)" @@ -16286,15 +16178,15 @@ msgstr "Moderni" #: GraphView/avatars.py:65 msgid "Generic (dark)" -msgstr "" +msgstr "Yleinen (tumma)" #: GraphView/avatars.py:66 msgid "Generic (gray)" -msgstr "" +msgstr "Yleinen (harmaa)" #: GraphView/avatars.py:67 msgid "Generic (light)" -msgstr "" +msgstr "Yleinen (vaalea)" #: GraphView/graphview.gpr.py:4 GraphView/graphview.gpr.py:14 #: GraphView/graphview.py:189 @@ -16737,80 +16629,76 @@ msgstr "Kumppanit" #: HasTagSubstr/hastagsubstr.gpr.py:25 HasTagSubstr/hastagsubstr.py:93 msgid "People with a tag containing " -msgstr "" +msgstr "Poimii henkilöt, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:26 HasTagSubstr/hastagsubstr.py:94 msgid "Matches people with a tag whose name contains the given substring" -msgstr "" +msgstr "Poimii henkilöt, joiden nimeen sisältyy annettu merkkijono" #: HasTagSubstr/hastagsubstr.gpr.py:40 HasTagSubstr/hastagsubstr.py:102 msgid "Families with a tag containing " -msgstr "" +msgstr "Perheet, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:42 HasTagSubstr/hastagsubstr.py:104 -#, fuzzy -#| msgid "Matches families that are matched by an event filter" msgid "Matches families with a tag whose name contains the given substring" -msgstr "Poimii perheet, joilla on suotimen mukainen tapahtuma" +msgstr "Perheet, joiden tagi sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:57 HasTagSubstr/hastagsubstr.py:113 msgid "Events with a tag containing " -msgstr "" +msgstr "Tapahtumat, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:59 HasTagSubstr/hastagsubstr.py:115 msgid "Matches events with a tag whose name contains the given substring" -msgstr "" +msgstr "Poimii tapahtumat, joiden nimi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:74 HasTagSubstr/hastagsubstr.py:124 msgid "Places with a tag containing " -msgstr "" +msgstr "Paikat, joiden tag sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:76 HasTagSubstr/hastagsubstr.py:126 msgid "Matches places with a tag whose name contains the given substring" -msgstr "" +msgstr "Paikat, joiden tag sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:91 HasTagSubstr/hastagsubstr.py:135 msgid "Sources with a tag containing " -msgstr "" +msgstr "Lähteet, joiden tag sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:93 HasTagSubstr/hastagsubstr.py:137 -#, fuzzy -#| msgid "Matches Sources with values containing the chosen parameters" msgid "Matches sources with a tag whose name contains the given substring" -msgstr "Poimii lähteet, jotka sisältävät valitun arvon" +msgstr "Lähteet, joiden tag sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:108 HasTagSubstr/hastagsubstr.py:146 msgid "Citations with a tag containing " -msgstr "" +msgstr "Viitteet, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:110 HasTagSubstr/hastagsubstr.py:148 msgid "Matches citations with a tag whose name contains the given substring" -msgstr "" +msgstr "Viitteet, joiden tagi sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:125 HasTagSubstr/hastagsubstr.py:157 msgid "Repositories with a tag containing " -msgstr "" +msgstr "Arkistot, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:127 HasTagSubstr/hastagsubstr.py:159 msgid "Matches repositories with a tag whose name contains the given substring" -msgstr "" +msgstr "Arkistot, joiden tagi sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:142 HasTagSubstr/hastagsubstr.py:168 msgid "Media objects with a tag containing " -msgstr "" +msgstr "Mediaobjektit, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:144 HasTagSubstr/hastagsubstr.py:170 msgid "" "Matches media objects with a tag whose name contains the given substring" -msgstr "" +msgstr "Mediaobjektit, joiden tagi sisältää annetun merkkijonon" #: HasTagSubstr/hastagsubstr.gpr.py:159 HasTagSubstr/hastagsubstr.py:179 msgid "Notes with a tag containing " -msgstr "" +msgstr "Lisätiedot, joiden tagi sisältää merkkijonon " #: HasTagSubstr/hastagsubstr.gpr.py:161 HasTagSubstr/hastagsubstr.py:181 msgid "Matches notes with a tag whose name contains the given substring" -msgstr "" +msgstr "Lisätiedot, joiden tagi sisältää annetun merkkijonon" #: HeadlineNewsGramplet/HeadlineNewsGramplet.gpr.py:4 #: HeadlineNewsGramplet/HeadlineNewsGramplet.gpr.py:11 From 640b22299fc8af5dce0233746fbf3d803283f347 Mon Sep 17 00:00:00 2001 From: Juan Saavedra Date: Wed, 5 Aug 2026 20:02:10 +0200 Subject: [PATCH 104/156] Translated using Weblate (Spanish) Currently translated at 60.4% (3356 of 5549 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/es/ --- po/es.po | 162 +++++++++++++++++++------------------------------------ 1 file changed, 55 insertions(+), 107 deletions(-) diff --git a/po/es.po b/po/es.po index 419aa9dbe..1381e3144 100644 --- a/po/es.po +++ b/po/es.po @@ -18,8 +18,8 @@ msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-03 09:18-0700\n" -"PO-Revision-Date: 2026-06-27 05:51+0000\n" -"Last-Translator: Libre <6n0n1m0s@proton.me>\n" +"PO-Revision-Date: 2026-07-24 05:14+0000\n" +"Last-Translator: Juan Saavedra \n" "Language-Team: Spanish \n" "Language: es\n" @@ -27,7 +27,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" @@ -264,40 +264,33 @@ msgstr "" #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:24 #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:32 -#, fuzzy -#| msgid "Inverse" msgid "Anniversaries" -msgstr "Inverso" +msgstr "Aniversarios" #: AnniversariesGramplet/AnniversariesGramplet.gpr.py:25 -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "A gramplet that displays the anniversaries of events" -msgstr "un gramplet que exhibe los cumpleaños de la gente viva" +msgstr "Un gramplete que muestra los aniversarios de eventos" #: AnniversariesGramplet/AnniversariesGramplet.py:52 -#, fuzzy -#| msgid "Double-click on a row to edit the selected participant." msgid "Double-click on a row to edit the event." -msgstr "Pulse dos veces en una fila para editar el participante seleccionado." +msgstr "Haga doble clic sobre una fila para editar el evento." #: AnniversariesGramplet/AnniversariesGramplet.py:59 -#, fuzzy -#| msgid "Other participants" msgid "Participant" -msgstr "Otras participantes" +msgstr "Participante" #: ArchiveAssist/ArchiveAssist.gpr.py:22 msgid "" "Parses strings from Riksarkivet and ArkivDigital to create sources and " "citations." msgstr "" +"Analiza cadenas de Riksarkivet y ArkivDigital para crear fuentes y citas." #: ArchiveAssist/ArchiveAssist.gpr.py:26 #, fuzzy #| msgid "Archive file" msgid "Archive Assist" -msgstr "Archivar archivo" +msgstr "Asistente de archivo" #: AssociationsTool/associationstool.gpr.py:36 msgid "Check Associations data" @@ -1014,7 +1007,7 @@ msgstr "¡hecho!\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:432 #, python-format msgid "Skipped %d people due to errors (see log).\n" -msgstr "" +msgstr "Se omitieron %d personas debido a errores (ver log).\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:347 msgid "" @@ -1089,7 +1082,7 @@ msgstr "Se añadieron %d eventos." #: CalculateEstimatedDates/CalculateEstimatedDates.py:562 #, python-format msgid " (Skipped %d rows due to errors; see log.)" -msgstr "" +msgstr " (Se omitieron %d filas debido a errores; ver log.)" #: CalculateEstimatedDates/CalculateEstimatedDates.py:589 msgid "Estimated date" @@ -1162,30 +1155,24 @@ msgid "Send" msgstr "Enviar" #: ChatWithTree/ChatWithTree.py:163 -#, fuzzy -#| msgid "Chat with Tree initialized. Type /help for help." msgid "Chat with Tree initialized. Type /help for help." -msgstr "Charla con Árbol Inicializado. Teclee /help para ayuda." +msgstr "Chat con Árbol Inicializado. Teclee /help para ayuda." #: ChatWithTree/ChatWithTree.py:463 -#, fuzzy -#| msgid "" -#| "The ChatWithTree addon is not yet initialized. Please reload Gramps or " -#| "select a database." msgid "" "The ChatWithTree addon is not yet initialized. Please " "reload Gramps or select a database." msgstr "" -"La extensión ChatWithTree no está aún inicializado. Recargue Gramps o " -"seleccione una base de datos." +"La extensión ChatWithTree no está aún inicializada. " +"Recargue Gramps o seleccione una base de datos." #: ChatWithTree/ChatWithTree.py:472 msgid "The chatbot is currently processing a query. Please wait." -msgstr "" +msgstr "El chatbot está actualmente procesando una consulta. Por favor espere." #: ChatWithTree/ChatWithTree.py:499 msgid "An error occurred while processing your query." -msgstr "" +msgstr "Se produjo un error al procesar su consulta." #: ChatWithTree/chatwithllm.py:119 msgid "Tree: '{}'" @@ -2086,14 +2073,12 @@ msgstr "en" #: DataEntryGramplet/DataEntryGramplet.py:428 #: DataEntryGramplet/DataEntryGramplet.py:507 -#, fuzzy -#| msgid "Family Tree file" msgid "No Family Tree is open." -msgstr "Archivo del Árbol Familiar" +msgstr "No hay ningún Árbol Genealógico abierto." #: DataEntryGramplet/DataEntryGramplet.py:429 msgid "Please open a Family Tree to edit data." -msgstr "" +msgstr "Por favor abra un Árbol Genealógico para editar datos." #: DataEntryGramplet/DataEntryGramplet.py:446 #: DataEntryGramplet/DataEntryGramplet.py:578 @@ -2103,7 +2088,7 @@ msgstr "Editar datos abuelo: %s" #: DataEntryGramplet/DataEntryGramplet.py:508 msgid "Please open a Family Tree before adding a person." -msgstr "" +msgstr "Por favor abra un Árbol Genealógico antes de añadir una persona." #: DataEntryGramplet/DataEntryGramplet.py:524 msgid "Can't add new person." @@ -2264,54 +2249,40 @@ msgid "Deep Connections" msgstr "Conexiones Profundas" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:106 -#, fuzzy -#| msgid "Pause" msgid "⏸ Pause" -msgstr "Pausar" +msgstr "⏸ Pausa" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:108 msgid "Pause the current search" -msgstr "" +msgstr "Pausa la búsqueda en curso" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:111 msgid "▶ Continue" -msgstr "" +msgstr "▶ Continuar" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:114 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Continue searching for more relations" -msgstr "" -"Pausado.\n" -"Presione Continuar para buscar relaciones adicionales.\n" +msgstr "Continúe buscando más relaciones" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:118 -#, fuzzy -#| msgid "Copy" msgid "📋 Copy" -msgstr "Copiar" +msgstr "📋 Copiar" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:120 -#, fuzzy -#| msgid "Copy link to clipboard" msgid "Copy selected people to clipboard" -msgstr "Copiar enlace en el portapapeles" +msgstr "Copiar las personas seleccionadas al portapapeles" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:128 msgid "🗑 Clear" -msgstr "" +msgstr "🗑 Limpiar" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:129 msgid "Clear all results and reset" -msgstr "" +msgstr "Limpiar todos los resultados y restablecer" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:158 -#, fuzzy -#| msgid "Start type to search" msgid "Ready to search" -msgstr "Teclee el texto para buscar" +msgstr "Listo para buscar" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:185 #, python-brace-format @@ -2319,6 +2290,8 @@ msgid "" "Search Depth: {depth} | People Processed: {processed} | Queue Size: " "{queue_size}" msgstr "" +"Profundidad de búsqueda: {depth} | Personas procesadas: {processed} | Tamaño " +"de la cola: {queue_size}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:216 msgid "mentioned in note" @@ -2355,24 +2328,20 @@ msgstr "" " %s de " #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:336 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Home Person set" -msgstr "Ninguna Persona Activa fijada." +msgstr "Error: No se ha definido una Persona Inicial" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:341 msgid "No Active Person set." msgstr "Ninguna Persona Activa fijada." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:342 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Active Person set" -msgstr "Ninguna Persona Activa fijada." +msgstr "Error: no se ha definido una Persona Activa" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:347 msgid "Initializing search..." -msgstr "" +msgstr "Iniciando búsqueda..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:366 msgid "Looking for relationship between\n" @@ -2389,10 +2358,8 @@ msgid " %s (Active Person)...\n" msgstr " %s (Persona Activa)…\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:382 -#, fuzzy -#| msgid "Fetching records..." msgid "Searching for connections..." -msgstr "Registros obtenidos…" +msgstr "Buscando conexiones..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:402 #, python-format @@ -2412,46 +2379,29 @@ msgstr "" "Presione Continuar para buscar relaciones adicionales.\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:427 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Paused - Press Continue to search for more relations" -msgstr "" -"Pausado.\n" -"Presione Continuar para buscar relaciones adicionales.\n" +msgstr "Pausado - Presione Continuar para buscar más relaciones" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:456 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." +#, python-format msgid "" "\n" "Search completed. %d relation paths found." msgstr "" "\n" -"Búsqueda completada. %d relaciones encontradas." +"Búsqueda completada. %d caminos de relación encontrados." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:460 -#, fuzzy -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." msgid "Search completed - {} relation paths found" -msgstr "" -"\n" -"Búsqueda completada. %d relaciones encontradas." +msgstr "Búsqueda completada - {} caminos de relación encontrados" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:471 msgid "Error during search: {}" msgstr "Error durante la búsqueda: {}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:481 -#, fuzzy -#| msgid "Removing '%s'..." msgid "Resuming search..." -msgstr "Retira «%s»…" +msgstr "Reanudando búsqueda..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:489 msgid "Search interrupted by user" @@ -3715,22 +3665,20 @@ msgstr "" " 0 = sin límite " #: DescendantsLines/DescendantsLines.py:1618 -#, fuzzy -#| msgid "Font name" msgid "Font Name" -msgstr "Nombre de tipo de letra" +msgstr "Nombre de la Fuente" #: DescendantsLines/DescendantsLines.py:1620 msgid "" "Name of the Font to use. On Windows enter the file name of the .ttf at /" "Windows/Fonts" msgstr "" +"Nombre de la Fuente a usar. En Windows ingrese el nombre del archivo .ttf " +"en /Windows/Fonts" #: DescendantsLines/DescendantsLines.py:1623 -#, fuzzy -#| msgid "Database size" msgid "Font base size" -msgstr "Tamaño de base de datos" +msgstr "Tamaño de base de la Fuente" #: DescendantsLines/DescendantsLines.py:1629 msgid "Box around Person's block" @@ -5802,7 +5750,7 @@ msgstr "" #: FamilySheet/FamilySheet.py:232 FamilySheet/FamilySheet.py:275 #, python-format msgid "→ %s" -msgstr "" +msgstr "→ %s" #: FamilySheet/FamilySheet.py:397 #, python-format @@ -6398,17 +6346,15 @@ msgstr "" #: FilterRules/matchpersonfilterrole.gpr.py:7 msgid "Events from people with role" -msgstr "" +msgstr "Eventos de las personas con rol" #: FilterRules/matchpersonfilterrole.gpr.py:8 -#, fuzzy -#| msgid "Matches people with an event with a selected role" msgid "Matches event of people filter with role" -msgstr "Personas coinciden con un evento con un rol seleccionado" +msgstr "Coincide con eventos de personas filtradas con rol" #: FilterRules/matchpersonfilterrole.py:69 msgid "Role:" -msgstr "" +msgstr "Rol:" #: FilterRules/multipleparents.gpr.py:27 FilterRules/multipleparents.gpr.py:28 msgid "Multiple Parents Filter" @@ -6569,6 +6515,8 @@ msgid "" "Check whether any Census events are missing for a person and some of their " "descendents" msgstr "" +"Revisar si hay eventos de Censo faltantes para una persona y algunos de sus " +"descendientes" #: Form/CensusCheckQuickview.gpr.py:23 #, fuzzy @@ -6581,11 +6529,13 @@ msgid "" "Check whether any Census events are missing for a person and some of their " "ancestors" msgstr "" +"Revisar si hay eventos de Censo faltantes para una persona y algunos de sus " +"ancestros" #: Form/CensusCheckQuickview.py:122 Form/CensusCheckUpQuickview.py:122 #, python-format msgid "Census Check for %s" -msgstr "" +msgstr "Revisión de Censo para %s" #: Form/editform.py:192 #, python-format @@ -6608,7 +6558,7 @@ msgstr "Referencia:" #: Form/editform.py:393 msgid "[Source recreated after deletion mid-form-edit]" -msgstr "" +msgstr "[Fuente recreada tras su eliminación durante la edición del formulario]" #: Form/editform.py:446 msgid "Headings" @@ -6620,13 +6570,11 @@ msgstr "Formulario" #: Form/form.py:174 msgid "XML syntax error in Form definition file" -msgstr "" +msgstr "Error de sintaxis XML en el archivo de definición de la Forma" #: Form/form.py:185 -#, fuzzy -#| msgid "Failed to read proto file %s: %s" msgid "Failed to read Form definition file" -msgstr "No se pudo leer el protoarchivo %s: %s" +msgstr "Falló al leer el archivo de definición de la Forma" #: Form/form.py:194 #, fuzzy From 3f3536744461f3ef061e481b54faa51fff49a54d Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Aug 2026 20:02:13 +0200 Subject: [PATCH 105/156] Update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/ --- po/ar.po | 196 ++++++++++++++++++++++++++------- po/bg.po | 207 ++++++++++++++++++++++++++++------- po/ca.po | 218 ++++++++++++++++++++++++++++++------- po/cs.po | 222 ++++++++++++++++++++++++++++++------- po/cy.po | 194 ++++++++++++++++++++++++++------- po/da.po | 256 +++++++++++++++++++++++++++++++++++-------- po/de.po | 272 +++++++++++++++++++++++++++++++++++++--------- po/el.po | 196 ++++++++++++++++++++++++++------- po/en_GB.po | 206 ++++++++++++++++++++++++++++------- po/eo.po | 196 ++++++++++++++++++++++++++------- po/es.po | 241 +++++++++++++++++++++++++++++++++-------- po/fi.po | 267 ++++++++++++++++++++++++++++++++++++--------- po/fr.po | 261 ++++++++++++++++++++++++++++++++++++-------- po/he.po | 272 ++++++++++++++++++++++++++++++++++++++-------- po/hr.po | 261 ++++++++++++++++++++++++++++++++++++-------- po/hu.po | 222 ++++++++++++++++++++++++++++++------- po/is.po | 196 ++++++++++++++++++++++++++------- po/it.po | 265 +++++++++++++++++++++++++++++++++++++-------- po/ja.po | 209 ++++++++++++++++++++++++++++------- po/ka.po | 194 ++++++++++++++++++++++++++------- po/ln.po | 194 ++++++++++++++++++++++++++------- po/lt.po | 230 ++++++++++++++++++++++++++++++++------- po/lv.po | 194 ++++++++++++++++++++++++++------- po/mn.po | 194 ++++++++++++++++++++++++++------- po/nb.po | 234 ++++++++++++++++++++++++++++++++------- po/ne.po | 194 ++++++++++++++++++++++++++------- po/nl.po | 280 ++++++++++++++++++++++++++++++++++++++--------- po/nn.po | 202 +++++++++++++++++++++++++++------- po/oc.po | 194 ++++++++++++++++++++++++++------- po/pl.po | 240 ++++++++++++++++++++++++++++++++-------- po/pt_BR.po | 227 +++++++++++++++++++++++++++++++------- po/pt_PT.po | 263 ++++++++++++++++++++++++++++++++++++-------- po/ru.po | 261 +++++++++++++++++++++++++++++++++++--------- po/sk.po | 272 +++++++++++++++++++++++++++++++++++++--------- po/sl.po | 205 ++++++++++++++++++++++++++++------- po/sq.po | 205 ++++++++++++++++++++++++++++------- po/sr.po | 196 ++++++++++++++++++++++++++------- po/sv.po | 267 ++++++++++++++++++++++++++++++++++++--------- po/tr.po | 307 ++++++++++++++++++++++++++++++++++++++++++---------- po/uk.po | 263 ++++++++++++++++++++++++++++++++++++-------- po/vi.po | 200 +++++++++++++++++++++++++++------- po/zh_CN.po | 214 ++++++++++++++++++++++++++++-------- po/zh_HK.po | 196 ++++++++++++++++++++++++++------- po/zh_TW.po | 196 ++++++++++++++++++++++++++------- 44 files changed, 8089 insertions(+), 1890 deletions(-) diff --git a/po/ar.po b/po/ar.po index 455698fbf..0ff18841e 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps-4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2014-06-29 15:50+0300\n" "Last-Translator: Munzir Taha (منذر طه) \n" "Language-Team: Arabic <>\n" @@ -23,6 +23,33 @@ msgstr "" "X-Generator: Lokalize 1.5\n" "X-Poedit-SourceCharset: utf-8\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -714,23 +741,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "بحث عن حقول المكان" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1203,6 +1213,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2355,12 +2369,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5106,6 +5114,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15585,10 +15611,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18636,6 +18658,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19017,6 +19043,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24194,25 +24265,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "فحص عناوين المكان" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/bg.po b/po/bg.po index c51bbc850..007e31486 100644 --- a/po/bg.po +++ b/po/bg.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-03-14 05:09+0000\n" "Last-Translator: Iskren Petkov \n" "Language-Team: Bulgarian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2397,12 +2411,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5191,6 +5199,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15709,10 +15735,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18812,6 +18834,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19204,6 +19230,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relative" +msgid "Person Relationship Filter" +msgstr "Роднини" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relative" +msgid "Relationship Filter" +msgstr "Роднини" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24431,28 +24506,73 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Създаване на филтър за ограничаване на хората" +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Проверка заглавията на места" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -24867,6 +24987,11 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Създаване на филтър за ограничаване на хората" + #~ msgid "Primary Name" #~ msgstr "Главно име" diff --git a/po/ca.po b/po/ca.po index 434a28bd0..5ce351115 100644 --- a/po/ca.po +++ b/po/ca.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: ca\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-09-03 03:01+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Catalan Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2460,12 +2476,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Cerca" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5364,6 +5374,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Persona GEXF" @@ -15997,10 +16025,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 #, fuzzy #| msgid "No changes" @@ -19191,6 +19215,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19608,6 +19636,59 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "No relation to active person" +msgid "Person Relationship Filter" +msgstr "No hi ha relació amb la persona activa" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "No relation to active person" +msgid "Relationship Filter" +msgstr "No hi ha relació amb la persona activa" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "germà" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "germà" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24976,28 +25057,75 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Seleccionar filtre per restringir persones" +#| msgid "Places tool" +msgid "Place Word Cloud" +msgstr "Eina de llocs" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of Persons: %s" +msgid "Number of %s" +msgstr "Nombre de Persones: %s" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing Files" +msgid "[Missing %s]" +msgstr "Fitxers mancants" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25506,6 +25634,14 @@ msgstr "" " \"%s\"\n" " al teu navegador web preferit..." +#~ msgid "Search" +#~ msgstr "Cerca" + +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Seleccionar filtre per restringir persones" + #~ msgid "Primary Name" #~ msgstr "Nom principal" diff --git a/po/cs.po b/po/cs.po index 7bc42d858..149a91ac9 100644 --- a/po/cs.po +++ b/po/cs.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.2.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-29 14:56+0000\n" "Last-Translator: Milan \n" "Language-Team: Czech Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2478,12 +2494,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Vyhledávání" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5390,6 +5400,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 #, fuzzy #| msgid "Persons" @@ -16047,10 +16075,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -19266,6 +19290,12 @@ msgstr "Stránka osoby" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Person page" +msgid "Overview" +msgstr "Stránka osoby" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19684,6 +19714,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Vztah k otci" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Vztah k otci" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25080,30 +25159,78 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Kontrolují se názvy míst" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format #| msgid "Number of pages" -msgid "Number of font sizes" +msgid "Number of %s" msgstr "Počet stránek" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Vyberte filtr pro omezení osob" +#| msgid "No color" +msgid "Hover color" +msgstr "Bez barev" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include references in indexes" +msgid "Click place name to view references" +msgstr "Zahrnout odkazy v indexech" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25591,6 +25718,19 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Vyhledávání" + +#, fuzzy +#~| msgid "Number of pages" +#~ msgid "Number of font sizes" +#~ msgstr "Počet stránek" + +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Vyberte filtr pro omezení osob" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" diff --git a/po/cy.po b/po/cy.po index 88095c306..a392bea4f 100644 --- a/po/cy.po +++ b/po/cy.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,6 +17,33 @@ msgstr "" "Plural-Forms: nplurals=6; plural=(n==0) ? 0 : (n==1) ? 1 : (n==2) ? 2 : " "(n==3) ? 3 :(n==6) ? 4 : 5;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -706,23 +733,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1193,6 +1203,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2345,12 +2359,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5096,6 +5104,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15573,10 +15599,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18620,6 +18642,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19001,6 +19027,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24172,25 +24243,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/da.po b/po/da.po index 12f134f88..6660ad37d 100644 --- a/po/da.po +++ b/po/da.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-23 14:01+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish vælg " "Proband." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Tilføjer mærkater til familie (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2527,12 +2548,6 @@ msgstr "af" msgid "the chart type runs out of bounds" msgstr "grafen kommer udenfor grænserne" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Søg" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso Tilvalg" @@ -5483,6 +5498,24 @@ msgstr "Tillad regex" msgid "Allow regular expressions." msgstr "Tillad regular expressions." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "GEXF for person" @@ -16115,10 +16148,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Sammenføj" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokale ændringer" @@ -19429,6 +19458,10 @@ msgstr "Person Oversigt" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet viser en oversigt over hændelser for en person" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Oversigt" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Familie Oversigt" @@ -19851,6 +19884,61 @@ msgstr "Mærkat farve og prioritet" msgid "No source information found" msgstr "Ingen kildeinformation fundet" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Slægtskab med fader" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet der viser slægtskabet mellem slægtninge" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Slægtskab med fader" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "søskende" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "søskende" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25533,26 +25621,85 @@ msgstr "Vis alt" msgid "Display Icons" msgstr "Visningstilstand" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet der viser slægtskabet mellem slægtninge" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet der benyttes til at danne ordskyer fra wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet der viser slægtskabet mellem slægtninge" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place title" +msgid "Place Word Cloud" +msgstr "Stedtitel" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet der viser slægtskabet mellem slægtninge" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Antal sider" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "No color" +msgid "Hover color" +msgstr "Ingen farve" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Mangler]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Antal af fontstørrelser" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Medtag Billede kilde henvisning" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Vælg filter til afgrænse listen" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26065,6 +26212,24 @@ msgstr "" " \"%s\"\n" " i din foretrukne web navigator ..." +#~ msgid "Search" +#~ msgstr "Søg" + +#~ msgid "Merge" +#~ msgstr "Sammenføj" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet der benyttes til at danne ordskyer fra wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Antal af fontstørrelser" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Vælg filter til afgrænse listen" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26116,9 +26281,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Eksporter" -#~ msgid "Overview" -#~ msgstr "Oversigt" - #~ msgid "No file parsed..." #~ msgstr "Ingen fil blev fortolket..." diff --git a/po/de.po b/po/de.po index 44b0b356a..796214ec1 100644 --- a/po/de.po +++ b/po/de.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-05 22:48+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German Hauptperson " "festlegen." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Hinzufügen von Etiketten zur Familie (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2543,12 +2565,6 @@ msgstr "von" msgid "the chart type runs out of bounds" msgstr "der Diagrammtyp überschreitet die Grenzwerte" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Suche" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso Optionen" @@ -5563,6 +5579,24 @@ msgstr "Reguläre Ausdrücke zulassen" msgid "Allow regular expressions." msgstr "Reguläre Ausdrücke zulassen." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Person GEXF" @@ -16061,8 +16095,8 @@ msgid "" "and model before chatting.\n" msgstr "" "\n" -"Es wurde kein Modell konfiguriert. Bitte klicke auf die Schaltfläche „" -"Einstellungen“, um vor dem Chatten ein Backend und ein Modell auszuwählen.\n" +"Es wurde kein Modell konfiguriert. Bitte klicke auf die Schaltfläche " +"„Einstellungen“, um vor dem Chatten ein Backend und ein Modell auszuwählen.\n" #: GrampsAssistant/grampsassistant.py:755 msgid "Thinking..." @@ -16118,8 +16152,9 @@ msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" -"URL eines lokalen OpenAI-kompatiblen Servers. Ollama: http://localhost:11434" -" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +"URL eines lokalen OpenAI-kompatiblen Servers. Ollama: http://" +"localhost:11434 LM Studio: http://localhost:1234 llama.cpp: http://" +"localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" @@ -16377,10 +16412,6 @@ msgstr "Zurücksetzen von entfernt auf lokal" msgid "Reset local to remote" msgstr "Lokal auf entfernt zurücksetzen" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Zusammenführen" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokale Änderungen" @@ -19670,6 +19701,10 @@ msgstr "Personenübersicht" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet zeigt eine Übersicht der Ereignisse für eine Person" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Übersicht" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Familienübersicht" @@ -20081,6 +20116,67 @@ msgstr "Farbe und Priorität des Etiketts" msgid "No source information found" msgstr "Keine Quelleninformation gefunden" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Beziehung zum Vater" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Beziehung zum Vater" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Tote Kinder" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "Geschwister" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "Geschwister" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Tote Kinder" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Tote Kinder" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25677,26 +25773,87 @@ msgstr "Spalten anzeigen" msgid "Display Icons" msgstr "Symbole anzeigen" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet verwendet, um Wortwolken mit wordle.net zu erstellen" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Ortsgeschichte geladen" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Anzahl der Seiten" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Farbig männlich" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Haarfarbe" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Fehlt]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Anzahl der Schriftgrößen" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Bild Quellenreferenzen aufnehmen" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Filter auswählen, um Liste einzuschränken" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26150,6 +26307,24 @@ msgstr "" " „%s“\n" " in deinem bevorzugten Web- Browser zu öffnen ..." +#~ msgid "Search" +#~ msgstr "Suche" + +#~ msgid "Merge" +#~ msgstr "Zusammenführen" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet verwendet, um Wortwolken mit wordle.net zu erstellen" + +#~ msgid "Number of font sizes" +#~ msgstr "Anzahl der Schriftgrößen" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Filter auswählen, um Liste einzuschränken" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26211,9 +26386,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exportieren" -#~ msgid "Overview" -#~ msgstr "Übersicht" - #~ msgid "No file parsed..." #~ msgstr "Keine Datei analysiert..." diff --git a/po/el.po b/po/el.po index fc5474b65..d928985a5 100644 --- a/po/el.po +++ b/po/el.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.0.3.\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-12-23 02:37+0000\n" "Last-Translator: klak kloyk \n" "Language-Team: Greek Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2352,12 +2366,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5103,6 +5111,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15582,10 +15608,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18634,6 +18656,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19015,6 +19041,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24195,25 +24266,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Έλεγχος τίτλων τοποθεσίας" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/en_GB.po b/po/en_GB.po index 420525e5d..cf5d283f5 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -28,7 +28,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.5.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-11 13:01+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom) Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2411,12 +2425,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Search" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5221,6 +5229,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15734,10 +15760,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Merge" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Local changes" @@ -18814,6 +18836,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19203,6 +19229,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Destination File" +msgid "Person Relationship Filter" +msgstr "Destination File" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Destination File" +msgid "Relationship Filter" +msgstr "Destination File" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24416,25 +24491,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place titles" +msgid "Place Word Cloud" +msgstr "Place titles" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 @@ -24853,6 +24975,12 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Search" + +#~ msgid "Merge" +#~ msgstr "Merge" + #~ msgid "Primary Name" #~ msgstr "Primary Name" diff --git a/po/eo.po b/po/eo.po index 2c3efd9cb..2e4680c04 100644 --- a/po/eo.po +++ b/po/eo.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-08-29 20:30+0000\n" "Last-Translator: jmichault \n" "Language-Team: Esperanto Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2383,12 +2397,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5134,6 +5142,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15615,10 +15641,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18669,6 +18691,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19056,6 +19082,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24233,25 +24304,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Loka historio ŝarĝita" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/es.po b/po/es.po index 1381e3144..88a083e8c 100644 --- a/po/es.po +++ b/po/es.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-24 05:14+0000\n" "Last-Translator: Juan Saavedra \n" "Language-Team: Spanish , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Persona GEXF" @@ -6558,7 +6590,8 @@ msgstr "Referencia:" #: Form/editform.py:393 msgid "[Source recreated after deletion mid-form-edit]" -msgstr "[Fuente recreada tras su eliminación durante la edición del formulario]" +msgstr "" +"[Fuente recreada tras su eliminación durante la edición del formulario]" #: Form/editform.py:446 msgid "Headings" @@ -16196,10 +16229,6 @@ msgstr "Restablecer remoto a local" msgid "Reset local to remote" msgstr "Restablecer local a remoto" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Fusionar" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Cambios locales" @@ -19543,6 +19572,10 @@ msgstr "Visión general de persona" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet mostrando una visión general de los eventos para una persona" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Descripción general" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Panorama general de la familia" @@ -19970,6 +20003,61 @@ msgstr "Color de etiqueta y prioridad" msgid "No source information found" msgstr "No se ha encontrado información de origen" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Relación con el padre" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet mostrando relativas dentro de una relación" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Relación con el padre" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "hermano" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "hermano" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25558,26 +25646,85 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet mostrando relativas dentro de una relación" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet mostrando relativas dentro de una relación" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Lugar de historia cargado" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet mostrando relativas dentro de una relación" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Número de páginas" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "No color" +msgid "Hover color" +msgstr "Sin color" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Seleccione un filtro para restringir la lista" +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing %s: %s" +msgid "[Missing %s]" +msgstr "Falta %s: %s" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Incluye referencias de origen de Imagen" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26088,6 +26235,15 @@ msgstr "" " «%s»\n" " en su navegador web preferido…" +#~ msgid "Search" +#~ msgstr "Búsqueda" + +#~ msgid "Merge" +#~ msgstr "Fusionar" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Seleccione un filtro para restringir la lista" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26149,9 +26305,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exportar" -#~ msgid "Overview" -#~ msgstr "Descripción general" - #~ msgid "No file parsed..." #~ msgstr "No se ha analizado ningún archivo..." diff --git a/po/fi.po b/po/fi.po index dc4910586..865e9d7d6 100644 --- a/po/fi.po +++ b/po/fi.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: fi\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-26 17:40+0000\n" "Last-Translator: Juha Mäkeläinen \n" "Language-Team: Finnish Aseta kotihenkilö." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Lisää tageja perheelle (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2495,12 +2517,6 @@ msgstr "/" msgid "the chart type runs out of bounds" msgstr "kaaviotyyppi ylittää rajat" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Hae" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoVison asetukset" @@ -5415,6 +5431,24 @@ msgstr "Salli säännölliset lausekkeet" msgid "Allow regular expressions." msgstr "Salli säännölliset lausekkeet." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Henkilö GEXF" @@ -15808,9 +15842,9 @@ msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" -"Paikallispalvelimelta pyydettävä malli. Pakollinen Ollamalle (esim. llama3.1)" -". Jätä tyhjäksi LM Studiolle tai llama.cpp-tiedostolle, jotka käyttävät " -"ladattua mallia." +"Paikallispalvelimelta pyydettävä malli. Pakollinen Ollamalle (esim. " +"llama3.1). Jätä tyhjäksi LM Studiolle tai llama.cpp-tiedostolle, jotka " +"käyttävät ladattua mallia." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 @@ -16042,10 +16076,6 @@ msgstr "Palauta etätiedot paikallisen mukaiseksi" msgid "Reset local to remote" msgstr "Palauta paikailliset tiedot etätietojen mukaisiksi" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Yhdistä" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Paikalliset muutokset" @@ -19318,6 +19348,10 @@ msgstr "Henkilön yleiskatsaus" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet näyttää yleiskatsauksen henkilön tapahtumista" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Yleiskatsaus" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Perheen yleiskatsaus" @@ -19746,6 +19780,67 @@ msgstr "Tagin väri ja prioriteetti" msgid "No source information found" msgstr "Ei lähdetietoja" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Suhde isään" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet näyttää suhteessa olevia sukulaisia" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Suhde isään" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Kuolleita lapsia" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "sisarus" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "sisarus" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Kuolleita lapsia" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Kuolleita lapsia" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25285,27 +25380,85 @@ msgstr "Näytä sarakkeet" msgid "Display Icons" msgstr "Näytä kuvakkeet" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet näyttää suhteessa olevia sukulaisia" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -"Grampletti, jota on käytetty sanapilvien tekemiseen wordle.net-sivustolla" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet näyttää suhteessa olevia sukulaisia" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Paikkahistoria ladattu" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet näyttää suhteessa olevia sukulaisia" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Sivujen määrä" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Hiusten väri" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Puuttuu]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Kirjainkokojen määrä" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Näytä kuvien lähdeviitteet" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Valitse suodatin luettelon rajaamiseksi" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25819,6 +25972,25 @@ msgstr "" " \"%s\"\n" " suosikkiselaimessasi ..." +#~ msgid "Search" +#~ msgstr "Hae" + +#~ msgid "Merge" +#~ msgstr "Yhdistä" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "" +#~ "Grampletti, jota on käytetty sanapilvien tekemiseen wordle.net-sivustolla" + +#~ msgid "Number of font sizes" +#~ msgstr "Kirjainkokojen määrä" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Valitse suodatin luettelon rajaamiseksi" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -25880,9 +26052,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Vie" -#~ msgid "Overview" -#~ msgstr "Yleiskatsaus" - #~ msgid "No file parsed..." #~ msgstr "Tiedostoa ei jäsennetty..." diff --git a/po/fr.po b/po/fr.po index cf0b90b1f..0dfaf2199 100644 --- a/po/fr.po +++ b/po/fr.po @@ -39,7 +39,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-01 15:01+0000\n" "Last-Translator: \"David D.\" \n" "Language-Team: French Définir comme souche." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Ajout d’étiquettes à la famille (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2547,12 +2569,6 @@ msgstr "de" msgid "the chart type runs out of bounds" msgstr "le type de graphique est hors limites" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Rechercher" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Options DenominoViso" @@ -5608,6 +5624,24 @@ msgstr "Permettre regex" msgid "Allow regular expressions." msgstr "Autorise les expressions régulières." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Individu GEXF" @@ -16317,10 +16351,6 @@ msgstr "Réinitialiser du distant vers le local" msgid "Reset local to remote" msgstr "Réinitialiser l'arbre local depuis l'arbre distant" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Fusion" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Changements locaux" @@ -19694,6 +19724,10 @@ msgstr "Aperçu d'individu" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet montrant un aperçu des événements pour un individu" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Aperçu" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Aperçu familial" @@ -20120,6 +20154,67 @@ msgstr "Couleur de l'étiquette et priorité" msgid "No source information found" msgstr "Pas d'information de la source trouvée" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Relation au père" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet affichant les parentés dans une relation" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Relation au père" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children who have Died." +msgid "Children of name match" +msgstr "Enfants décédés." + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "le frère ou la sœur" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "le frère ou la sœur" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Died" +msgid "Child 1" +msgstr "Enfants décédés" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Died" +msgid "Child 2" +msgstr "Enfants décédés" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25699,26 +25794,85 @@ msgstr "Afficher les colonnes" msgid "Display Icons" msgstr "Afficher les icônes" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet affichant les parentés dans une relation" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet utilisé pour faire des nuages de mots avec wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet affichant les parentés dans une relation" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Historique du lieu chargé" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet affichant les parentés dans une relation" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Nombre de pages" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "No color" +msgid "Hover color" +msgstr "Aucune couleur" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Absent]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Nombre de tailles de police" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Inclure ou non les sources de l'image" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Sélectionnez un filtre pour restreindre la liste" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26200,6 +26354,24 @@ msgstr "" " \"%s\"\n" " dans votre navigateur internet préféré ..." +#~ msgid "Search" +#~ msgstr "Rechercher" + +#~ msgid "Merge" +#~ msgstr "Fusion" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet utilisé pour faire des nuages de mots avec wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Nombre de tailles de police" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Sélectionnez un filtre pour restreindre la liste" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26259,9 +26431,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exporter" -#~ msgid "Overview" -#~ msgstr "Aperçu" - #~ msgid "No file parsed..." #~ msgstr "Aucun fichier analysé ..." diff --git a/po/he.po b/po/he.po index 5465b0b7e..d3469d48f 100644 --- a/po/he.po +++ b/po/he.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-29 16:56+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "אדם GEXF" @@ -15949,10 +15983,6 @@ msgstr "שיצוב מצב מרוחק למקומי" msgid "Reset local to remote" msgstr "שיצוב מצב מקומי למרוחק" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "מיזוג" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "שינויים מקומיים" @@ -19125,6 +19155,12 @@ msgstr "אדם סקירה" msgid "Gramplet showing an overview of events for a person" msgstr "גרמפלט להצגת תקציר אירועים לאדם" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Person Overview" +msgid "Overview" +msgstr "אדם סקירה" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "סקירת משפחה" @@ -19521,6 +19557,67 @@ msgstr "צבע תג ועדיפות" msgid "No source information found" msgstr "לא נמצא מידע מקור" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "יוחסה לאב" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "יוחסה לאב" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "ילדים נפטר" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "אחאי" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "אחאי" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "ילדים נפטר" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "ילדים נפטר" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -21009,11 +21106,11 @@ msgid "" "Assistant in order to export all data.\n" "\n" msgstr "" -"גיבוי ל־Gramps XML בגרסאות גרמפס " -"האחרונות, 'יצירת גיבוי...' נמצא בתפריט אילן־יוחסין. אחרת יש להשתמש ב" -"־'ייצוא...' באותו תפריט, אך להסיר את סימון אפשרויות הפרטיות בסייען הייצוא " -"כדי לייצא את כל הנתונים.\n" +"גיבוי ל־Gramps XML בגרסאות גרמפס האחרונות, " +"'יצירת גיבוי...' נמצא בתפריט אילן־יוחסין. אחרת יש להשתמש ב־'ייצוא...' באותו " +"תפריט, אך להסיר את סימון אפשרויות הפרטיות בסייען הייצוא כדי לייצא את כל " +"הנתונים.\n" "\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:336 @@ -24980,26 +25077,87 @@ msgstr "הצגת עמודות" msgid "Display Icons" msgstr "הצגת סמלים" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "היסטוריית מקום נטענה" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "מספר עמודים" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "צבעוני זכר" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "גרמפלט ליצירת ענני מילים באמצעות wordle.net" +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "צבע שיער" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[חסר]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "מספר גדלי גופנים" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "בחירת מסנן להגבלת רשימה" +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "הכללת איזכורי מקור תמונה" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25459,6 +25617,24 @@ msgstr "" " '%s'\n" " בדפדפן המרשתת המועדף..." +#~ msgid "Search" +#~ msgstr "חיפוש" + +#~ msgid "Merge" +#~ msgstr "מיזוג" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "גרמפלט ליצירת ענני מילים באמצעות wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "מספר גדלי גופנים" + +#~ msgid "Select filter to restrict list" +#~ msgstr "בחירת מסנן להגבלת רשימה" + #~ msgid "Why, when same code works in Graphview" #~ msgstr "מדוע, כאשר אותו קוד עובד ב־Graphview" diff --git a/po/hr.po b/po/hr.po index 07d18afff..5f87396be 100644 --- a/po/hr.po +++ b/po/hr.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-05-17 15:49+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: Croatian =20) ? 1 : 2);\n" "X-Generator: Weblate 2026.6.dev0\n" +msgid "Birthdays" +msgstr "Rođendani" + +msgid "Ignore birthdays with tag" +msgstr "Zanemari rođendane s oznakom" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "Prikaži samo rođendane s oznakom" + +msgid "Proximity to current date" +msgstr "" + +#, fuzzy +#| msgid "Sort by " +msgid "Sort birthdays by" +msgstr "Razvrstaj po " + +#, fuzzy +#| msgid "Birth date of deceased" +msgid "Sort dates of death by" +msgstr "Datum rođenja preminule osobe" + +#, fuzzy +#| msgid "a gramplet that displays the birthdays of the living people" +msgid "a gramplet that displays death dates in sorted order" +msgstr "gramplet prikazuje rođendane živih osoba" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "gramplet prikazuje rođendane živih osoba" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "Sva imena svih osoba" @@ -775,23 +808,6 @@ msgstr "Uredi obitelji" msgid "Looking for children birth order" msgstr "Pretraga redoslijeda rođenja djece" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "Rođendani" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "gramplet prikazuje rođendane živih osoba" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "Zanemari rođendane s oznakom" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "Prikaži samo rođendane s oznakom" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1299,6 +1315,12 @@ msgstr "" "„Osobe”, odaberi osobu koju želiš imati kao „početnu osobu”, zatim potvrdi " "tvoj izbor putem izbornika „Uredi > Postavi početnu osobu”." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Dodavanje oznaka obitelji (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2489,12 +2511,6 @@ msgstr "od" msgid "the chart type runs out of bounds" msgstr "vrsta dijagrama prelazi ograničenja" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Traži" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Opcije za DenominoViso" @@ -5425,6 +5441,24 @@ msgstr "Dozvoli regularne izraze" msgid "Allow regular expressions." msgstr "Dozvoli regularne izraze." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Osoba GEXF" @@ -16087,10 +16121,6 @@ msgstr "Resetiraj ne-lokalne u lokalne podatke" msgid "Reset local to remote" msgstr "Resetiraj lokalne u ne-lokalne podatke" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Spoji" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokalne promjene" @@ -19381,6 +19411,10 @@ msgstr "Pregled osobe" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet prikazuje pregled događaja za jednu osobu" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Pregled" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Pregled obitelji" @@ -19803,6 +19837,67 @@ msgstr "Boja oznake i prioritet" msgid "No source information found" msgstr "Podaci o izvoru nisu pronađeni" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Srodstvo s ocem" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet za prikaz veza srodnika" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Srodstvo s ocem" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Total" +msgid "Children of name match" +msgstr "Ukupno djece" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "brat/sestra" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "brat/sestra" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Died" +msgid "Child 1" +msgstr "Umrla djeca" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Died" +msgid "Child 2" +msgstr "Umrla djeca" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25380,26 +25475,85 @@ msgstr "Prikaži stupce" msgid "Display Icons" msgstr "Prikaži ikone" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet za prikaz veza srodnika" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet za izradu oblaka s riječima pomoću wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet za prikaz veza srodnika" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Povijest mjesta učitana" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet za prikaz veza srodnika" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Broj stranica" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Boja kose" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Nedostaje]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Broj veličina fonta" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Uključi reference izvora slika" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Odaberi filtar za ograničavanje popisa" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25861,6 +26015,24 @@ msgstr "" " „%s”\n" "u tvom omiljenom web pregledniku …" +#~ msgid "Search" +#~ msgstr "Traži" + +#~ msgid "Merge" +#~ msgstr "Spoji" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet za izradu oblaka s riječima pomoću wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Broj veličina fonta" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Odaberi filtar za ograničavanje popisa" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -25922,9 +26094,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Izvezi" -#~ msgid "Overview" -#~ msgstr "Pregled" - #~ msgid "No file parsed..." #~ msgstr "Nijedna datoteka nije analizirana …" diff --git a/po/hu.po b/po/hu.po index b218a8dd3..ded5d1526 100644 --- a/po/hu.po +++ b/po/hu.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: hu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-05 13:54+0000\n" "Last-Translator: Milan \n" "Language-Team: Hungarian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2527,12 +2543,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Keresés" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5467,6 +5477,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 #, fuzzy #| msgid "Persons" @@ -16173,10 +16201,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -19394,6 +19418,12 @@ msgstr "Személy oldala" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Person page" +msgid "Overview" +msgstr "Személy oldala" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19808,6 +19838,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Kapcsolat az apával" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Kapcsolat az apával" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25215,30 +25294,78 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Helynév ellenőrzése" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format #| msgid "Number of pages" -msgid "Number of font sizes" +msgid "Number of %s" msgstr "Oldalak száma" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Szűrő kiválasztása személyek kizárására" +#| msgid "Colors" +msgid "Hover color" +msgstr "Színek" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include references in indexes" +msgid "Click place name to view references" +msgstr "Hivatkozásokkal a tárgymutatóban" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25718,6 +25845,19 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Keresés" + +#, fuzzy +#~| msgid "Number of pages" +#~ msgid "Number of font sizes" +#~ msgstr "Oldalak száma" + +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Szűrő kiválasztása személyek kizárására" + #~ msgid "Primary Name" #~ msgstr "Elsődleges név" diff --git a/po/is.po b/po/is.po index e3aa4fd73..35880b66c 100644 --- a/po/is.po +++ b/po/is.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-04-30 20:09+0000\n" "Last-Translator: Sveinn í Felli \n" "Language-Team: Icelandic Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2348,12 +2362,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5101,6 +5109,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15582,10 +15608,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Breytingar á staðnum" @@ -18630,6 +18652,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19013,6 +19039,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24186,25 +24257,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Saga staðar hlaðin" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/it.po b/po/it.po index 832e204f2..c6211503d 100644 --- a/po/it.po +++ b/po/it.po @@ -66,7 +66,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-09 15:32+0000\n" "Last-Translator: Paolo Zamponi \n" "Language-Team: Italian Imposta " "persona principale." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Aggiunta di etichette alla famiglia (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2562,12 +2584,6 @@ msgstr "di" msgid "the chart type runs out of bounds" msgstr "il tipo di grafico esce dai limiti" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Ricerca" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5503,6 +5519,24 @@ msgstr "Permetti espressione regolare" msgid "Allow regular expressions." msgstr "Permette le espressioni reegolari." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -16135,10 +16169,6 @@ msgstr "Reimposta da remoto a locale" msgid "Reset local to remote" msgstr "Reimposta da locale a remoto" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Fondi" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Modifiche locali" @@ -19361,6 +19391,12 @@ msgstr "Panoramica della persona" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet che mostra una panoramica degli eventi di una persona" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Person Overview" +msgid "Overview" +msgstr "Panoramica della persona" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Panoramica della famiglia" @@ -19752,6 +19788,65 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Relazione con il padre" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Relazione con il padre" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Figli morti" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "fratello o sorella" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "fratello o sorella" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Figli morti" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Figli morti" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -21156,12 +21251,12 @@ msgid "" "Assistant in order to export all data.\n" "\n" msgstr "" -"Backup in formato XML di Gramps " -"Nelle versioni recenti di Gramps, l'opzione “Crea backup...” si trova nel " -"menu Albero genealogico; in caso contrario, utilizzare “Esporta...” nello " -"stesso menu, ma deselezionando le opzioni relative alla privacy " -"nell'Assistente all'esportazione per poter esportare tutti i dati. \n" +"Backup in formato XML di Gramps Nelle " +"versioni recenti di Gramps, l'opzione “Crea backup...” si trova nel menu " +"Albero genealogico; in caso contrario, utilizzare “Esporta...” nello stesso " +"menu, ma deselezionando le opzioni relative alla privacy nell'Assistente " +"all'esportazione per poter esportare tutti i dati. \n" "\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:336 @@ -25138,26 +25233,85 @@ msgstr "Mostra colonne" msgid "Display Icons" msgstr "Mostra icone" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing the references for a source" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet che mostra i riferimenti relativi a una fonte" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing the references for a source" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet che mostra i riferimenti relativi a una fonte" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Cronologia dei luoghi caricata" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing the references for a source" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet che mostra i riferimenti relativi a una fonte" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Numero di pagine" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Colore dei capelli" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Mancante]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Totale dimensioni dei caratteri" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Inserire i riferimenti alle fonti delle immagini" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Seleziona filtro per restringere gli elenchi" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25610,6 +25764,21 @@ msgstr "" " \"%s\"\n" " nel tuo navigatore web preferito ..." +#~ msgid "Search" +#~ msgstr "Ricerca" + +#~ msgid "Merge" +#~ msgstr "Fondi" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Number of font sizes" +#~ msgstr "Totale dimensioni dei caratteri" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Seleziona filtro per restringere gli elenchi" + #~ msgid "Primary Name" #~ msgstr "Nome primario" diff --git a/po/ja.po b/po/ja.po index a900c0791..1c0942fee 100644 --- a/po/ja.po +++ b/po/ja.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 3.3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-03-24 10:31+0000\n" "Last-Translator: coolz daddy \n" "Language-Team: Japanese 「ホーム人物を設" "定」で選択内容を確認します。" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "家族 (%s) にタグを追加" + #: CombinedView/personpage.py:597 #, fuzzy, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2485,12 +2501,6 @@ msgstr "の" msgid "the chart type runs out of bounds" msgstr "チャートの種類が範囲外です" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "検索" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso オプション" @@ -5350,6 +5360,24 @@ msgstr "正規表現を許可する" msgid "Allow regular expressions." msgstr "正規表現を許可します。" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "人物 GEXF" @@ -15875,10 +15903,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18987,6 +19011,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19396,6 +19424,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "父親との関係" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "父親との関係" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24688,25 +24765,76 @@ msgstr "表示モード" msgid "Display Icons" msgstr "表示モード" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "場所のタイトルをチェック" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "ページ数" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing %s: %s" +msgid "[Missing %s]" +msgstr "欠落 %s : %s" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "画像ソースの参照を含める" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 @@ -25206,6 +25334,9 @@ msgstr "" " \"%s\"\n" " を開いてみてください ..." +#~ msgid "Search" +#~ msgstr "検索" + #~ msgid "Select All" #~ msgstr "すべて選択" diff --git a/po/ka.po b/po/ka.po index 934cb39b4..ccd05e09b 100644 --- a/po/ka.po +++ b/po/ka.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-09-13 09:49+0000\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2349,12 +2363,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5100,6 +5108,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15577,10 +15603,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18625,6 +18647,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19006,6 +19032,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24177,25 +24248,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/ln.po b/po/ln.po index f2a0defbe..31e0e552c 100644 --- a/po/ln.po +++ b/po/ln.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -16,6 +16,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -705,23 +732,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1192,6 +1202,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2344,12 +2358,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5095,6 +5103,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15572,10 +15598,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18619,6 +18641,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19000,6 +19026,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24171,25 +24242,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/lt.po b/po/lt.po index fbef93209..cccda82c2 100644 --- a/po/lt.po +++ b/po/lt.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: lt\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-01-04 20:01+0000\n" "Last-Translator: openSUSE Lietuviškai \n" "Language-Team: Lithuanian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2441,12 +2461,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Paieška" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5258,6 +5272,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15812,10 +15844,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Apjungti" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18959,6 +18987,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19375,6 +19407,59 @@ msgstr "Gairės spalva ir pirmenybė" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relation type" +msgid "Person Relationship Filter" +msgstr "Ryšio rūšis" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relation type" +msgid "Relationship Filter" +msgstr "Ryšio rūšis" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "Show siblings" +msgid "Sibling 1" +msgstr "Rodyti brolius ir seseris" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "Show siblings" +msgid "Sibling 2" +msgstr "Rodyti brolius ir seseris" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24631,26 +24716,77 @@ msgstr "Rodyti stulpelius" msgid "Display Icons" msgstr "Rodyti piktogramas" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Vietovės istorija įkelta" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Puslapių skaičius" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Plaukų spalva" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing %s: %s" +msgid "[Missing %s]" +msgstr "Trūksta %s: %s" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Šrifto dydžių skaičius" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Pasirinkite filtrą, kad apribotumėte sąrašą" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25133,6 +25269,18 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Paieška" + +#~ msgid "Merge" +#~ msgstr "Apjungti" + +#~ msgid "Number of font sizes" +#~ msgstr "Šrifto dydžių skaičius" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Pasirinkite filtrą, kad apribotumėte sąrašą" + #~ msgid "Primary Name" #~ msgstr "Pagrindinis vardas" diff --git a/po/lv.po b/po/lv.po index 419f6931b..b2a202744 100644 --- a/po/lv.po +++ b/po/lv.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,6 +17,33 @@ msgstr "" "Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= " "19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -706,23 +733,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1193,6 +1203,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2345,12 +2359,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5096,6 +5104,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15573,10 +15599,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18621,6 +18643,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19002,6 +19028,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24173,25 +24244,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/mn.po b/po/mn.po index e4f1223ba..05b746dc7 100644 --- a/po/mn.po +++ b/po/mn.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-04-07 13:46+0000\n" "Last-Translator: \"Batsaihan P.\" \n" "Language-Team: Mongolian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2358,12 +2372,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5109,6 +5117,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15586,10 +15612,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18639,6 +18661,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19024,6 +19050,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24195,25 +24266,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/nb.po b/po/nb.po index ac4be0314..75262b79a 100644 --- a/po/nb.po +++ b/po/nb.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: nb\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-01 12:35+0000\n" "Last-Translator: Harald Herreros \n" "Language-Team: Norwegian Bokmål Gjør til " "startperson." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2463,12 +2481,6 @@ msgstr "av" msgid "the chart type runs out of bounds" msgstr "tavletypen går utenfor grensene" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Søk" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5270,6 +5282,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15798,10 +15828,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokale endringer" @@ -18889,6 +18915,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet som viser en oversikt over hendelser for en person" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19273,6 +19303,61 @@ msgstr "" msgid "No source information found" msgstr "Ingen kildeinformasjon funnet" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Slektskap til far" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet som viser beslektede slektninger" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Slektskap til far" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "søsken" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "søsken" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24492,26 +24577,89 @@ msgstr "Vis kolonner" msgid "Display Icons" msgstr "Vis ikoner" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet som viser beslektede slektninger" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet som viser beslektede slektninger" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Stedshistorikk innlastet" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet som viser beslektede slektninger" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "" +#| "Number of tags : \n" +#| "\t\t\t%06s\n" +msgid "Number of %s" +msgstr "" +"Antall merker : \n" +"\t\t\t%06s\n" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair Color" +msgid "Hover color" +msgstr "Hårfarge" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing header" +msgid "[Missing %s]" +msgstr "Mangler overskrift" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Velg filter for å begrense listen" +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Inkluder kildehenvisninger for bilder" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -24958,6 +25106,12 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Søk" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Velg filter for å begrense listen" + #, python-format #~ msgid "Found %d custom event types" #~ msgstr "Fant %d tilpassede hendelsestyper" diff --git a/po/ne.po b/po/ne.po index 1d74f149c..dac9bc69c 100644 --- a/po/ne.po +++ b/po/ne.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -16,6 +16,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -705,23 +732,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1192,6 +1202,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2344,12 +2358,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5095,6 +5103,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15572,10 +15598,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18619,6 +18641,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19000,6 +19026,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24171,25 +24242,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/nl.po b/po/nl.po index 1b79d6ddb..7d02ff345 100644 --- a/po/nl.po +++ b/po/nl.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-04 17:49+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch Centrale persoon instellen." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Labels aan gezin (%s) toevoegen" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2520,12 +2543,6 @@ msgstr "van" msgid "the chart type runs out of bounds" msgstr "het grafiektype loopt buiten de grenzen" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Zoeken" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso opties" @@ -5521,6 +5538,24 @@ msgstr "Regex toestaan" msgid "Allow regular expressions." msgstr "Sta reguliere expressies toe." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Persoon GEXF" @@ -16041,8 +16076,9 @@ msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" -"URL van een lokale OpenAI-compatibele server. Ollama: http://localhost:11434" -" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +"URL van een lokale OpenAI-compatibele server. Ollama: http://" +"localhost:11434 LM Studio: http://localhost:1234 llama.cpp: http://" +"localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" @@ -16053,9 +16089,9 @@ msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" -"Model op te vragen bij de lokale server. Vereist voor Ollama (bijv. llama3.1)" -". Laat leeg voor LM Studio of llama.cpp, die welk model dan ook wordt " -"geladen gebruiken." +"Model op te vragen bij de lokale server. Vereist voor Ollama (bijv. " +"llama3.1). Laat leeg voor LM Studio of llama.cpp, die welk model dan ook " +"wordt geladen gebruiken." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 @@ -16294,10 +16330,6 @@ msgstr "Server opnieuw instellen vanuit lokaal" msgid "Reset local to remote" msgstr "Lokaal opnieuw instellen vanuit server" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Samenvoegen" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokale wijzigingen" @@ -18637,7 +18669,8 @@ msgstr "Patroniemsuggestie" #: NameSuite/name_processor.gpr.py:27 msgid "Suggests (East Slavic) patronymic names in real-time as you navigate." -msgstr "Stelt realtime (Oost-Slavisch) patroniemnamen voor terwijl u navigeert." +msgstr "" +"Stelt realtime (Oost-Slavisch) patroniemnamen voor terwijl u navigeert." #: NameSuite/name_processor/views/base_tab.py:150 msgid "Use" @@ -19573,6 +19606,10 @@ msgstr "Persoonsoverzicht" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet met een overzicht van gebeurtenissen voor een persoon" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Overzicht" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Gezinsoverzicht" @@ -19979,6 +20016,67 @@ msgstr "Labelkleur en prioriteit" msgid "No source information found" msgstr "Geen broninformatie gevonden" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Verwantschap met vader" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet toont verwanten in een relatie" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Verwantschap met vader" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "OverledenKinderen" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "broer/zus" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "broer/zus" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "OverledenKinderen" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "OverledenKinderen" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25562,26 +25660,87 @@ msgstr "Kolomen weergeven" msgid "Display Icons" msgstr "Pictogrammen weergeven" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet gebruikt om woordwolken te maken met wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet toont verwanten in een relatie" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet toont verwanten in een relatie" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Plaatsgeschiedenis geladen" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet toont verwanten in een relatie" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Aantal pagina's" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Gekleurde man" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Haarkleur" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Ontbreekt]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Aantal lettergroottes" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Selecteer een filter om de lijst te beperken" +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Voeg bronvermeldingen voor afbeeldingen toe" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26034,6 +26193,24 @@ msgstr "" " \"%s\" te openen\n" " in uw favoriete webnavigator..." +#~ msgid "Search" +#~ msgstr "Zoeken" + +#~ msgid "Merge" +#~ msgstr "Samenvoegen" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet gebruikt om woordwolken te maken met wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Aantal lettergroottes" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Selecteer een filter om de lijst te beperken" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26095,9 +26272,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exporteren" -#~ msgid "Overview" -#~ msgstr "Overzicht" - #~ msgid "No file parsed..." #~ msgstr "Geen bestand geparseerd ..." diff --git a/po/nn.po b/po/nn.po index 1199bab08..c86a1a925 100644 --- a/po/nn.po +++ b/po/nn.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: nn\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2014-12-22 21:26+0100\n" "Last-Translator: \n" "Language-Team: Norwegian Nynorsk \n" @@ -20,6 +20,33 @@ msgstr "" "X-Generator: Lokalize 1.5\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -718,23 +745,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "Ser etter felt for stad" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1209,6 +1219,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2365,12 +2379,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5185,6 +5193,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15701,10 +15727,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18797,6 +18819,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19196,6 +19222,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Description: " +msgid "Person Relationship Filter" +msgstr "Omtale: " + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Description: " +msgid "Relationship Filter" +msgstr "Omtale: " + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24409,25 +24484,74 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Kontrollerer stadnamn" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Colors" +msgid "Hover color" +msgstr "Fargar" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/oc.po b/po/oc.po index e9ddf2d64..87bcc33a3 100644 --- a/po/oc.po +++ b/po/oc.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -16,6 +16,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -705,23 +732,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1192,6 +1202,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2344,12 +2358,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5095,6 +5103,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15572,10 +15598,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18619,6 +18641,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19000,6 +19026,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24171,25 +24242,70 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +msgid "Place Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/pl.po b/po/pl.po index 1eaae4293..56b35b2f3 100644 --- a/po/pl.po +++ b/po/pl.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-12-14 21:00+0000\n" "Last-Translator: WaldiS \n" "Language-Team: Polish Ustaw osobę domową." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Dodawanie tagów do rodziny (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2571,12 +2593,6 @@ msgstr "z" msgid "the chart type runs out of bounds" msgstr "typ wykresu wykracza poza granice" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Szukaj" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Opcje DenominoViso" @@ -5534,6 +5550,24 @@ msgstr "Zezwól na wyrażenia regularne" msgid "Allow regular expressions." msgstr "Zezwala na używanie wyrażeń regularnych." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Osoba GEXF" @@ -16187,10 +16221,6 @@ msgstr "Resetuj zdalne do lokalnego" msgid "Reset local to remote" msgstr "Resetuj lokalne do zdalnego" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Scal" - #: GrampsWebSync/grampswebsync.py:948 #, fuzzy #| msgid "No changes" @@ -19497,6 +19527,10 @@ msgstr "Przegląd osoby" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet pokazujący przegląd zdarzeń dla danej osoby" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Opis" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19943,6 +19977,59 @@ msgstr "" msgid "No source information found" msgstr "Format źródła" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Relacja do ojca" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Relacja do ojca" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "rodzeństwo" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "rodzeństwo" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25484,28 +25571,85 @@ msgstr "Typ wyświetlania" msgid "Display Icons" msgstr "Typ wyświetlania" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing an overview of events for a person" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet pokazujący przegląd zdarzeń dla danej osoby" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Places tool" +msgid "Place Word Cloud" +msgstr "Narzędzie miejsc" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet for showing people and descendant counts" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet do wyświetlania osób i liczby potomków" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Liczba stron" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Colored Mężczyźni" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Wybierz filtr, aby ograniczyć listę osób" +#| msgid "No color" +msgid "Hover color" +msgstr "Bez koloru" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing %s: %s" +msgid "[Missing %s]" +msgstr "Brakuje %s: %s" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Określa, czy dołączać odnośniki do źródeł dla obrazów." + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26012,6 +26156,17 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "Szukaj" + +#~ msgid "Merge" +#~ msgstr "Scal" + +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Wybierz filtr, aby ograniczyć listę osób" + #~ msgid "Primary Name" #~ msgstr "Podstawowa nazwa" @@ -26045,9 +26200,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Eksportuj" -#~ msgid "Overview" -#~ msgstr "Opis" - #~ msgid "No file parsed..." #~ msgstr "Nie przetworzono pliku..." diff --git a/po/pt_BR.po b/po/pt_BR.po index e7beb781c..ea51b7aa9 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Andre Magri \n" "Language-Team: Portuguese (Brazil) Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2519,12 +2535,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5516,6 +5526,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -16234,10 +16262,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -19473,6 +19497,12 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Family Tree file" +msgid "Overview" +msgstr "Arquivo de árvore genealógica" + #: Overview/Overview.gpr.py:45 #, fuzzy #| msgid "Family Tree file" @@ -19902,6 +19932,61 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "No relation to active person" +msgid "Person Relationship Filter" +msgstr "Nenhuma relação para a pessoa ativa" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet for showing people and descendant counts" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet para mostrar a quantidade de pessoas e descendentes" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "No relation to active person" +msgid "Relationship Filter" +msgstr "Nenhuma relação para a pessoa ativa" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "irmã(o)" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "irmã(o)" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25358,28 +25443,83 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet for showing people and descendant counts" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet para mostrar a quantidade de pessoas e descendentes" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet for showing people and descendant counts" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet para mostrar a quantidade de pessoas e descendentes" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Verificando os nomes dos locais" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet for showing people and descendant counts" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet para mostrar a quantidade de pessoas e descendentes" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Selecione o filtro para restringir as pessoas" +#| msgid "No color" +msgid "Hover color" +msgstr "Sem cor" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include items that are different" +msgid "Click place name to view references" +msgstr "Incluir os itens com diferenças" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25805,6 +25945,11 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Selecione o filtro para restringir as pessoas" + #~ msgid "Primary Name" #~ msgstr "Nome principal" diff --git a/po/pt_PT.po b/po/pt_PT.po index d29d2869d..b2bd9cbad 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-04 17:49+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) Definir indivíduo inicial." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Adicionar etiquetas à família (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2483,12 +2505,6 @@ msgstr "de" msgid "the chart type runs out of bounds" msgstr "o tipo de gráfico sai dos limites" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Procurar" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Opções do DenominoViso" @@ -5428,6 +5444,24 @@ msgstr "Permitir regex" msgid "Allow regular expressions." msgstr "Permite a utilização de expressões regulares." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Indivíduo GEXF" @@ -16155,10 +16189,6 @@ msgstr "Repor a remota com a local" msgid "Reset local to remote" msgstr "Repor a local com a remota" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Unir" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Alterações locais" @@ -19418,6 +19448,10 @@ msgstr "Visão geral do indivíduo" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet que mostra uma visão geral dos eventos de um dado indivíduo" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Visão geral" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Visão geral da família" @@ -19823,6 +19857,67 @@ msgstr "Cor e prioridade da etiqueta" msgid "No source information found" msgstr "Sem informações de fontes" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Relação com o pai" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet que mostra parentes numa relação" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Relação com o pai" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Filhos falecidos" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "irmão" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "irmão" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Filhos falecidos" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Filhos falecidos" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25378,26 +25473,87 @@ msgstr "Mostrar colunas" msgid "Display Icons" msgstr "Ícones de exibição" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet para construir nuvens de palavras com wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet que mostra parentes numa relação" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet que mostra parentes numa relação" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Histórico do local carregado" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet que mostra parentes numa relação" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Número de páginas" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "De cor masculino" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Cor do cabelo" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "(em falta)" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Número de tamanhos de letra" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Incluir referências à fonte das imagens" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Seleccione um filtro para limitar a lista" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25849,6 +26005,24 @@ msgstr "" " \"%s\"\n" " no seu navegador web favorito..." +#~ msgid "Search" +#~ msgstr "Procurar" + +#~ msgid "Merge" +#~ msgstr "Unir" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet para construir nuvens de palavras com wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Número de tamanhos de letra" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Seleccione um filtro para limitar a lista" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -25910,9 +26084,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exportar" -#~ msgid "Overview" -#~ msgstr "Visão geral" - #~ msgid "No file parsed..." #~ msgstr "Nenhum ficheiro analisado..." diff --git a/po/ru.po b/po/ru.po index 3828ce371..2281652e8 100644 --- a/po/ru.po +++ b/po/ru.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps50\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-09 08:01+0000\n" "Last-Translator: Vadim Barsukov \n" "Language-Team: Russian Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Изменение меток семьи (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2568,12 +2588,6 @@ msgstr "для" msgid "the chart type runs out of bounds" msgstr "тип графика выходит за границы" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Поиск" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Параметры DenominoViso" @@ -5555,6 +5569,24 @@ msgstr "Использовать регулярное выражение" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 #, fuzzy #| msgid "Persons" @@ -16231,12 +16263,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -#, fuzzy -#| msgid "Merge Media" -msgid "Merge" -msgstr "Объединение документов" - #: GrampsWebSync/grampswebsync.py:948 #, fuzzy #| msgid "No changes" @@ -19656,6 +19682,10 @@ msgstr "Обзор по лицу" msgid "Gramplet showing an overview of events for a person" msgstr "Отображает события связаные с человеком" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Обзор" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Обзор по семье" @@ -20076,6 +20106,61 @@ msgstr "Цвет метки и приоритет" msgid "No source information found" msgstr "Не найдено информации об источнике" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Отношение к отцу" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Отображение родственников в связях" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Отношение к отцу" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "брат/сестра" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "брат/сестра" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25905,26 +25990,85 @@ msgstr "Режим отображения" msgid "Display Icons" msgstr "Режим отображения" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Облако слов" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Отображение родственников в связях" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Грамплет создающий облако слов с помощью wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Отображение родственников в связях" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place title" +msgid "Place Word Cloud" +msgstr "Название места" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Отображение родственников в связях" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Количество страниц" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "No color" +msgid "Hover color" +msgstr "Без цвета" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Отсутствует]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Размер шрифта" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Выберите фильтр для сокращения списка" +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Включать ссылки на источник изображения" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26441,6 +26585,26 @@ msgstr "" " \"%s\"\n" " в Вашем браузере по умолчанию ..." +#~ msgid "Search" +#~ msgstr "Поиск" + +#, fuzzy +#~| msgid "Merge Media" +#~ msgid "Merge" +#~ msgstr "Объединение документов" + +#~ msgid "Wordle" +#~ msgstr "Облако слов" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Грамплет создающий облако слов с помощью wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Размер шрифта" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Выберите фильтр для сокращения списка" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26491,9 +26655,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Экспорт" -#~ msgid "Overview" -#~ msgstr "Обзор" - #~ msgid "No file parsed..." #~ msgstr "Нет разобранного файла..." diff --git a/po/sk.po b/po/sk.po index 5c6d3c34b..75a112379 100644 --- a/po/sk.po +++ b/po/sk.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-09 15:32+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak =2 && n<=4) ? 1 : 2);\n" "X-Generator: Weblate 2026.7.1.dev0\n" +msgid "Birthdays" +msgstr "Narodeniny" + +msgid "Ignore birthdays with tag" +msgstr "Ignorovať dni narodenia so štítkom" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "Zobraziť iba dni narodenia so štítkom" + +msgid "Proximity to current date" +msgstr "" + +#, fuzzy +#| msgid "Sort by " +msgid "Sort birthdays by" +msgstr "Zoradiť podľa " + +#, fuzzy +#| msgid "Birth date of deceased" +msgid "Sort dates of death by" +msgstr "Dátum narodenia zosnulého" + +#, fuzzy +#| msgid "a gramplet that displays the birthdays of the living people" +msgid "a gramplet that displays death dates in sorted order" +msgstr "Gramplet, ktorý zobrazuje narodeniny žijúcich ľudí" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "Gramplet, ktorý zobrazuje narodeniny žijúcich ľudí" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "Všetky mená všetkých ľudí" @@ -769,23 +802,6 @@ msgstr "Upraviť rodiny" msgid "Looking for children birth order" msgstr "Hľadanie poradia narodenia detí" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "Narodeniny" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "Gramplet, ktorý zobrazuje narodeniny žijúcich ľudí" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "Ignorovať dni narodenia so štítkom" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "Zobraziť iba dni narodenia so štítkom" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1299,6 +1315,12 @@ msgstr "" "Ľudia, vyberte osobu, ktorú chcete ako 'Domovskú osobu', a potom potvrďte " "svoj výber cez menu Upraviť -> Nastaviť domovskú osobu." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Pridávanie štítkov k rodine (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2486,12 +2508,6 @@ msgstr "z" msgid "the chart type runs out of bounds" msgstr "typ grafu presahuje hranice" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Vyhľadávanie" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Voľby DenominoViso" @@ -5425,6 +5441,24 @@ msgstr "Povoliť regex" msgid "Allow regular expressions." msgstr "Povoliť regulárne výrazy." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Osoba GEXF" @@ -15851,8 +15885,8 @@ msgid "" "tree. Use the ⚙ button to configure the AI.\n" msgstr "" "Opýtajte sa ma čokoľvek o programe Gramps alebo o vašom konkrétnom rodinnom " -"strome v Gramps. Použite tlačidlo ⚙ na konfiguráciu umelej inteligencie (AI)" -".\n" +"strome v Gramps. Použite tlačidlo ⚙ na konfiguráciu umelej inteligencie " +"(AI).\n" #: GrampsAssistant/grampsassistant.py:720 msgid "" @@ -15916,8 +15950,9 @@ msgid "" "URL of a local OpenAI-compatible server. Ollama: http://localhost:11434 LM " "Studio: http://localhost:1234 llama.cpp: http://localhost:8080" msgstr "" -"URL lokálneho servera kompatibilného s OpenAI. Ollama: http://localhost:11434" -" LM Studio: http://localhost:1234 llama.cpp: http://localhost:8080" +"URL lokálneho servera kompatibilného s OpenAI. Ollama: http://" +"localhost:11434 LM Studio: http://localhost:1234 llama.cpp: http://" +"localhost:8080" #: GrampsAssistant/grampsassistant.py:1257 msgid "model name (leave blank for LM Studio / llama.cpp)" @@ -16166,10 +16201,6 @@ msgstr "Obnoviť vzdialené na lokálne" msgid "Reset local to remote" msgstr "Obnoviť lokálne na vzdialené" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Zlúčiť" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokálne zmeny" @@ -19405,6 +19436,10 @@ msgstr "Osobný prehľad" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet zobrazujúci prehľad udalostí pre osobu" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Prehľad" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Rodinný prehľad" @@ -19810,6 +19845,67 @@ msgstr "Farba štítku a priorita" msgid "No source information found" msgstr "Neboli nájdené žiadne informácie o zdroji" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Vzťah k otcovi" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Vzťah k otcovi" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Deti zomreté" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "súrodenec" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "súrodenec" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Deti zomreté" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Deti zomreté" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25395,26 +25491,87 @@ msgstr "Zobraziť stĺpce" msgid "Display Icons" msgstr "Zobraziť ikony" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "História miesta načítaná" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Počet stránok" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Farebný muž" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Farba vlasov" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet používaný na vytváranie oblakov slov s wordle.net" +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "Chýbajúci]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Počet veľkostí písma" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Zahrnúť zdrojové referencie obrázkov" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Vyberte filter na vymedzenie zoznamu" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25870,6 +26027,24 @@ msgstr "" " \"%s\" \n" " do svojho preferovaného webového prehliadača ..." +#~ msgid "Search" +#~ msgstr "Vyhľadávanie" + +#~ msgid "Merge" +#~ msgstr "Zlúčiť" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet používaný na vytváranie oblakov slov s wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Počet veľkostí písma" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Vyberte filter na vymedzenie zoznamu" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -25931,9 +26106,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exportovať" -#~ msgid "Overview" -#~ msgstr "Prehľad" - #~ msgid "No file parsed..." #~ msgstr "Nebol analyzovaný žiadny súbor..." diff --git a/po/sl.po b/po/sl.po index db0aed45f..49f533a29 100644 --- a/po/sl.po +++ b/po/sl.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2008-02-22 23:19+0100\n" "Last-Translator: Bernard Banko \n" "Language-Team: lugos slovenizacija \n" @@ -22,6 +22,33 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || " "n%100==4 ? 3 : 0)\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -718,23 +745,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "Iskanje polj za kraje" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1236,6 +1246,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2392,12 +2406,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5213,6 +5221,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15723,10 +15749,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18813,6 +18835,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19203,6 +19229,53 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +msgid "Person Relationship Filter" +msgstr "V sorodu" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +msgid "Relationship Filter" +msgstr "V sorodu" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24421,28 +24494,73 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Izberi filter za omejitev oseb" +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Preverjanje nazivov krajev" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -24864,6 +24982,11 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Izberi filter za omejitev oseb" + #, fuzzy #~ msgid "Select All" #~ msgstr "Izberi datoteko" diff --git a/po/sq.po b/po/sq.po index baf505a46..7ac05c30f 100644 --- a/po/sq.po +++ b/po/sq.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2008-11-13 21:00+0100\n" "Last-Translator: Vlora Jakupi \n" "Language-Team: \n" @@ -32,6 +32,33 @@ msgstr "" "X-Poedit-Bookmarks: 3498,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-SearchPath-0: /home/jole/SVN/gramps/gramps30\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -728,23 +755,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "Kërkim i fushave të vendit" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1246,6 +1256,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2402,12 +2416,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5189,6 +5197,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15689,10 +15715,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18776,6 +18798,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19166,6 +19192,53 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +msgid "Person Relationship Filter" +msgstr "Të lidhur" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +msgid "Relationship Filter" +msgstr "Të lidhur" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24377,28 +24450,73 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "Përzgjedh filterin për të kufizuar njerëzit" +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Kontrrollimi i titullit të vendit" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" +msgstr "" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -24812,6 +24930,11 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "Përzgjedh filterin për të kufizuar njerëzit" + #, fuzzy #~ msgid "Select All" #~ msgstr "Përzgjedh skedar" diff --git a/po/sr.po b/po/sr.po index 75cf60282..e2014e9b3 100644 --- a/po/sr.po +++ b/po/sr.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-05-12 12:11+0000\n" "Last-Translator: Ранко Николић \n" "Language-Team: Serbian =20) ? 1 : 2);\n" "X-Generator: Weblate 2026.5-dev\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "Сва имена свих особа" @@ -713,23 +740,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "Тражим поља места" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1202,6 +1212,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2354,12 +2368,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5105,6 +5113,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15584,10 +15610,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18637,6 +18659,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19022,6 +19048,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24205,25 +24276,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Проверавам наслове места" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/sv.po b/po/sv.po index ce1985ed1..9382cec28 100644 --- a/po/sv.po +++ b/po/sv.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-05 22:49+0000\n" "Last-Translator: Pär Ekholm \n" "Language-Team: Swedish Välj Hemperson." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Lägger till taggar till familjen (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2490,12 +2512,6 @@ msgstr "av" msgid "the chart type runs out of bounds" msgstr "diagramtypen utanför gränser" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Sök" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso-alternativ" @@ -5419,6 +5435,24 @@ msgstr "Tillåt reguljära uttryck" msgid "Allow regular expressions." msgstr "Tillåt reguljära uttryck." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "GEXF för person" @@ -15826,9 +15860,9 @@ msgid "" "Model to request from the local server. Required for Ollama (e.g. llama3.1). " "Leave blank for LM Studio or llama.cpp, which use whatever model is loaded." msgstr "" -"Modell att begära från den lokala servern. Krävs för Ollama (t.ex. llama3.1)" -". Lämna tomt för LM Studio eller llama.cpp, som använder den modell som " -"laddas." +"Modell att begära från den lokala servern. Krävs för Ollama (t.ex. " +"llama3.1). Lämna tomt för LM Studio eller llama.cpp, som använder den modell " +"som laddas." #: GrampsAssistant/grampsassistant.py:1267 #: GrampsAssistant/grampsassistant.py:1311 @@ -16067,10 +16101,6 @@ msgstr "Återställ fjärr till lokalt läge" msgid "Reset local to remote" msgstr "Återställ lokalt till fjärrläge" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Slå samman" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Lokala ändringar" @@ -19362,6 +19392,10 @@ msgstr "Personöversikt" msgid "Gramplet showing an overview of events for a person" msgstr "Gramplet som visar en översikt på händelser för en person" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Översikt" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Familjeöversikt" @@ -19785,6 +19819,67 @@ msgstr "Taggfärg och prioritet" msgid "No source information found" msgstr "Ingen källinformation funnen" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Släktskap med far" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Gramplet som visar släktingar i en relation" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Släktskap med far" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Barn döda" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "syskon" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "syskon" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Barn döda" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Barn döda" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25301,26 +25396,85 @@ msgstr "Visa kolumner" msgid "Display Icons" msgstr "Visa ikoner" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Gramplet som visar släktingar i en relation" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "Gramplet för att skapa ordmoln med wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Gramplet som visar släktingar i en relation" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Platshistorik laddad" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Gramplet som visar släktingar i en relation" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Antal sidor" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Hårfärg" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Saknas]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Antal typsnittsstorlekar" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Huruvida ta med bildkällreferenser" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Välj ett filter för att begränsa lista" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25835,6 +25989,24 @@ msgstr "" "\"%s\"\n" "till din föredragna webbnavigator ..." +#~ msgid "Search" +#~ msgstr "Sök" + +#~ msgid "Merge" +#~ msgstr "Slå samman" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "Gramplet för att skapa ordmoln med wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Antal typsnittsstorlekar" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Välj ett filter för att begränsa lista" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -25893,9 +26065,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Exportera" -#~ msgid "Overview" -#~ msgstr "Översikt" - #~ msgid "No file parsed..." #~ msgstr "Ingen fil tolkades..." diff --git a/po/tr.po b/po/tr.po index 86a14960c..72e1d9c85 100644 --- a/po/tr.po +++ b/po/tr.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-07-26 17:41+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish Ana Kişiyi Ayarla menüsü aracılığıyla seçiminizi onaylayın." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Aileye etiket ekleme (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2501,12 +2523,6 @@ msgstr "<" msgid "the chart type runs out of bounds" msgstr "Grafik türü sınırların dışına çıkıyor" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Arama" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "DenominoViso Seçenekleri" @@ -5466,6 +5482,24 @@ msgstr "Düzenli ifadelere izin ver" msgid "Allow regular expressions." msgstr "Normal ifadelerin kullanılmasına izin verin." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Kişi GEXF" @@ -8103,7 +8137,8 @@ msgstr "Engeller" #: Form/form_ca.xml.h:527 msgid "34. Infirmities: a. Deaf and dumb; b. Blind; c. Unsound mind" -msgstr "34. Engeller: a. Sağır ve dilsiz; b. Kör; c. Akıl sağlığı yerinde değil" +msgstr "" +"34. Engeller: a. Sağır ve dilsiz; b. Kör; c. Akıl sağlığı yerinde değil" #: Form/form_ca.xml.h:538 msgid "Name from Schedule 1" @@ -8282,7 +8317,8 @@ msgstr "Kiralanmış Ahırlar" #: Form/form_ca.xml.h:586 msgid "23. Real Estate Leased: Number of barns, stables and other outbuildings" -msgstr "23. Kiralanan Gayrimenkuller: Ahır, at ahırı ve diğer müştemilat sayısı" +msgstr "" +"23. Kiralanan Gayrimenkuller: Ahır, at ahırı ve diğer müştemilat sayısı" #: Form/form_ca.xml.h:587 msgid "LeasedSilos" @@ -10791,7 +10827,8 @@ msgstr "Eğitimli profesyonel mühendisler" #: Form/form_us.xml.h:232 msgid "Names of pensioners for Revolutionary or military services" -msgstr "Devrimci savaş veya askerî hizmetler için emekli maaşı alanların adları" +msgstr "" +"Devrimci savaş veya askerî hizmetler için emekli maaşı alanların adları" #: Form/form_us.xml.h:233 msgid "Ages" @@ -16206,10 +16243,6 @@ msgstr "Uzak adresi yerel adrese sıfırla" msgid "Reset local to remote" msgstr "Yerel olanı uzak olana sıfırla" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Birleştir" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Yerel değişiklikler" @@ -16836,7 +16869,8 @@ msgstr " etiketi içeren kaynaklar" #: HasTagSubstr/hastagsubstr.gpr.py:93 HasTagSubstr/hastagsubstr.py:137 msgid "Matches sources with a tag whose name contains the given substring" -msgstr "Adında belirtilen alt dizeyi içeren etikete sahip kaynakları eşleştirir" +msgstr "" +"Adında belirtilen alt dizeyi içeren etikete sahip kaynakları eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:108 HasTagSubstr/hastagsubstr.py:146 msgid "Citations with a tag containing " @@ -16844,7 +16878,8 @@ msgstr " etiketi içeren alıntılar" #: HasTagSubstr/hastagsubstr.gpr.py:110 HasTagSubstr/hastagsubstr.py:148 msgid "Matches citations with a tag whose name contains the given substring" -msgstr "Alıntıları, adında belirtilen alt dizeyi içeren bir etiketle eşleştirir" +msgstr "" +"Alıntıları, adında belirtilen alt dizeyi içeren bir etiketle eşleştirir" #: HasTagSubstr/hastagsubstr.gpr.py:125 HasTagSubstr/hastagsubstr.py:157 msgid "Repositories with a tag containing " @@ -17790,7 +17825,8 @@ msgstr "Ölüm etiketi döndürme" #: LifeLineChartView/_dummy_translation_string_po.py:51 msgid "The death label is written in a text frame rotated by this value." -msgstr "Ölüm etiketi, bu değer kadar döndürülmüş bir metin çerçevesine yazılır." +msgstr "" +"Ölüm etiketi, bu değer kadar döndürülmüş bir metin çerçevesine yazılır." #: LifeLineChartView/_dummy_translation_string_po.py:53 msgid "Horizontal offset of death label" @@ -19460,6 +19496,12 @@ msgstr "Kişi Genel Bakışı" msgid "Gramplet showing an overview of events for a person" msgstr "Bir kişi için olayların genel görünümünü gösteren Gramplet" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +#, fuzzy +#| msgid "Person Overview" +msgid "Overview" +msgstr "Kişi Genel Bakışı" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Aile Genel Bakışı" @@ -19866,6 +19908,67 @@ msgstr "Etiket rengi ve önceliği" msgid "No source information found" msgstr "Kaynak bilgisi bulunamadı" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Baba ile ilişki" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Bir ilişkide akrabaları gösteren Gramplet" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Baba ile ilişki" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Ölen çocuklar" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "kardeş" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "kardeş" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Ölen çocuklar" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Ölen çocuklar" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -20521,7 +20624,8 @@ msgstr "Geçersiz enlem/boylam eşleştirme düzenli ifadesi" #: PlaceCompletion/PlaceCompletion.py:420 msgid "Non valid regular expression given to find lat/lon. Quiting." -msgstr "Enlem/boylam bulmak için geçersiz bir düzenli ifade verildi. Çıkılıyor." +msgstr "" +"Enlem/boylam bulmak için geçersiz bir düzenli ifade verildi. Çıkılıyor." #: PlaceCompletion/PlaceCompletion.py:479 msgid "Finding Places and appropriate changes" @@ -21088,7 +21192,8 @@ msgstr "PostgreSQL Veritabanı" #: PostgreSQLEnhanced/concurrency.py:479 #, python-brace-format msgid "Object {obj_type}:{handle} was modified by another user" -msgstr "{obj_type}:{handle} nesnesi başka bir kullanıcı tarafından değiştirildi" +msgstr "" +"{obj_type}:{handle} nesnesi başka bir kullanıcı tarafından değiştirildi" #: PostgreSQLEnhanced/migration.py:168 msgid "Starting migration..." @@ -21708,10 +21813,10 @@ msgid "" "_Command_Line#LANG.2C_LANGUAGE.2C_LC_MESSAGE.2C_LC_TIME\">Locale Settings:\n" msgstr "" -"Yerel Ayarlar:" -"\n" +"Yerel Ayarlar:\n" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 @@ -22951,8 +23056,8 @@ msgid "" "witness1, name, given, age, occupation; witness2, name, given, age, " "occupation, etc ..." msgstr "" -"tanık1, ad, verilen ad, yaş, meslek; tanık2, ad, verilen ad, yaş, meslek, vb " -"..." +"tanık1, ad, verilen ad, yaş, meslek; tanık2, ad, verilen ad, yaş, meslek, " +"vb ..." #: SourceIndex/index.glade:7 msgid "index" @@ -23280,7 +23385,8 @@ msgstr "Kişileri göster" #: SourcesCitationsReport/SourcesCitationsReport.py:444 msgid "Whether to show events and persons mentioned in the note" -msgstr "Notta bahsedilen etkinliklerin ve kişilerin gösterilip gösterilmeyeceği" +msgstr "" +"Notta bahsedilen etkinliklerin ve kişilerin gösterilip gösterilmeyeceği" #: SourcesCitationsReport/SourcesCitationsReport.py:495 msgid "The style used for the subtitle of the report." @@ -23835,7 +23941,8 @@ msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda bar mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:513 #, python-format msgid "%(female_name)s became a bar mitzvah in %(year)s at %(place)s." -msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda bar mitzvah oldu." +msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bar mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:516 #, python-format @@ -23845,7 +23952,8 @@ msgstr "%(male_name)s, %(year)s yılında %(place)s konumunda bat mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:518 #, python-format msgid "%(female_name)s became a bat mitzvah in %(year)s at %(place)s." -msgstr "%(female_name)s, %(year)s yılında %(place)s konumunda bat mitzvah oldu." +msgstr "" +"%(female_name)s, %(year)s yılında %(place)s konumunda bat mitzvah oldu." #: ThisDayInFamilyHistory/ThisDayInFamilyHistory.py:521 #, python-format @@ -25478,26 +25586,87 @@ msgstr "Sütunları görüntüle" msgid "Display Icons" msgstr "Simgeleri görüntüle" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Bir ilişkide akrabaları gösteren Gramplet" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Bir ilişkide akrabaları gösteren Gramplet" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Yer geçmişi yüklendi" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Bir ilişkide akrabaları gösteren Gramplet" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Sayfa sayısı" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Renkli Erkek" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" -msgstr "wordle.net ile kelime bulutları oluşturmak için kullanılan Gramplet" +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "No color" +msgid "Hover color" +msgstr "Renk yok" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Eksik]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Yazı tipi boyutu sayısı" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Listeyi kısıtlamak için filtre seçin" +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Görüntü kaynak referanslarını dahil et" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -25945,6 +26114,24 @@ msgstr "" " \"%s\"\n" " web gezgininde açmayı deneyin..." +#~ msgid "Search" +#~ msgstr "Arama" + +#~ msgid "Merge" +#~ msgstr "Birleştir" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "wordle.net ile kelime bulutları oluşturmak için kullanılan Gramplet" + +#~ msgid "Number of font sizes" +#~ msgstr "Yazı tipi boyutu sayısı" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Listeyi kısıtlamak için filtre seçin" + #~ msgid "Primary Name" #~ msgstr "Birincil ad" diff --git a/po/uk.po b/po/uk.po index ffa1ce6d0..566e8db94 100644 --- a/po/uk.po +++ b/po/uk.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2025-11-17 06:51+0000\n" "Last-Translator: Fedir Zinchuk \n" "Language-Team: Ukrainian =20) ? 1 : 2);\n" "X-Generator: Weblate 5.15-dev\n" +msgid "Birthdays" +msgstr "Дні народження" + +msgid "Ignore birthdays with tag" +msgstr "Ігнорувати дні народження з тегом" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "Показувати лише дні народження з тегом" + +msgid "Proximity to current date" +msgstr "" + +#, fuzzy +#| msgid "Sort by " +msgid "Sort birthdays by" +msgstr "Сортувати за " + +#, fuzzy +#| msgid "Birth date of deceased" +msgid "Sort dates of death by" +msgstr "Дата народження покійного" + +#, fuzzy +#| msgid "a gramplet that displays the birthdays of the living people" +msgid "a gramplet that displays death dates in sorted order" +msgstr "грамплет, що відображає дні народження живих людей" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "грамплет, що відображає дні народження живих людей" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "Всі імена всіх людей" @@ -809,23 +842,6 @@ msgstr "Редагувати сімʼї" msgid "Looking for children birth order" msgstr "Пошук порядку народження дітей" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "Дні народження" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "грамплет, що відображає дні народження живих людей" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "Ігнорувати дні народження з тегом" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "Показувати лише дні народження з тегом" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1338,6 +1354,12 @@ msgstr "" "виберіть особу, яку ви хочете як \"Головну особу\", потім підтвердіть свій " "вибір через меню Редагування -> Встановити Головну особу." +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +#, fuzzy +#| msgid "Adding Tags to family (%s)" +msgid "Add existing child to family" +msgstr "Додавання тегів до сім'ї (%s)" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2567,12 +2589,6 @@ msgstr "з" msgid "the chart type runs out of bounds" msgstr "тип діаграми виходить за межі" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "Пошук" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "Налаштування DenominoViso" @@ -5514,6 +5530,24 @@ msgstr "Дозволити регулярні вирази" msgid "Allow regular expressions." msgstr "Дозволити використання регулярних виразів." +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "Особа GEXF" @@ -16296,10 +16330,6 @@ msgstr "Скинути віддалені дані до локальних" msgid "Reset local to remote" msgstr "Скинути локальні дані до віддалених" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "Об'єднати" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "Локальні зміни" @@ -19609,6 +19639,10 @@ msgstr "Огляд особи" msgid "Gramplet showing an overview of events for a person" msgstr "Грамплет, що показує огляд подій для особи" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "Огляд" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "Огляд родини" @@ -20039,6 +20073,67 @@ msgstr "Колір мітки та пріоритет" msgid "No source information found" msgstr "Інформація про джерело не знайдена" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Person Relationship Filter" +msgstr "Відношення до батька" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet providing a person filter on relationships" +msgstr "Грамплет, що показує родичів у зв’язку" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relationship to Father" +msgid "Relationship Filter" +msgstr "Відношення до батька" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +#, fuzzy +#| msgid "Children Dead" +msgid "Children of name match" +msgstr "Діти, що померли" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 1" +msgstr "брат/сестра" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +#, fuzzy +#| msgid "sibling" +msgid "Sibling 2" +msgstr "брат/сестра" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 1" +msgstr "Діти, що померли" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +#, fuzzy +#| msgid "Children Dead" +msgid "Child 2" +msgstr "Діти, що померли" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -25561,27 +25656,87 @@ msgstr "Показати стовпці" msgid "Display Icons" msgstr "Значки дисплея" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" -msgstr "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all given names as a word cloud" +msgstr "Грамплет, що показує родичів у зв’язку" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all surnames as a word cloud" +msgstr "Грамплет, що показує родичів у зв’язку" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Place history loaded" +msgid "Place Word Cloud" +msgstr "Історія місць завантажена" + +#: WordClouds/WordClouds.gpr.py:57 +#, fuzzy +#| msgid "Gramplet showing relatives in a relation" +msgid "Gramplet showing all places as a word cloud" +msgstr "Грамплет, що показує родичів у зв’язку" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, fuzzy, python-format +#| msgid "Number of pages" +msgid "Number of %s" +msgstr "Кількість сторінок" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +#, fuzzy +#| msgid "Colored Male" +msgid "Color (low)" +msgstr "Темношкірий чоловік" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Hair color" +msgid "Hover color" +msgstr "Колір волосся" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" msgstr "" -"Gramplet використовується для створення хмар слів за допомогою wordle.net" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "[Missing]" +msgid "[Missing %s]" msgstr "[Відсутнє]" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" -msgstr "Розмір шрифту" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +#, fuzzy +#| msgid "Include Image source references" +msgid "Click place name to view references" +msgstr "Включити посилання на джерела зображень" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" -msgstr "Виберіть фільтр для обмеження списку" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -26099,6 +26254,25 @@ msgstr "" " \"%s\"\n" " у вашому улюбленому веб-навігаторі..." +#~ msgid "Search" +#~ msgstr "Пошук" + +#~ msgid "Merge" +#~ msgstr "Об'єднати" + +#~ msgid "Wordle" +#~ msgstr "Wordle" + +#~ msgid "Gramplet used to make word clouds with wordle.net" +#~ msgstr "" +#~ "Gramplet використовується для створення хмар слів за допомогою wordle.net" + +#~ msgid "Number of font sizes" +#~ msgstr "Розмір шрифту" + +#~ msgid "Select filter to restrict list" +#~ msgstr "Виберіть фільтр для обмеження списку" + #, python-format #~ msgid "\\u2192 %s" #~ msgstr "\\u2192 %s" @@ -26160,9 +26334,6 @@ msgstr "" #~ msgid "Export" #~ msgstr "Експорт" -#~ msgid "Overview" -#~ msgstr "Огляд" - #~ msgid "No file parsed..." #~ msgstr "Файл не розібрано…" diff --git a/po/vi.po b/po/vi.po index 404ad76e2..84ba367cf 100644 --- a/po/vi.po +++ b/po/vi.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VIETNAMESE 4.2.8\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-02-08 09:09+0000\n" "Last-Translator: Securitocat \n" "Language-Team: Vietnamese Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2360,12 +2374,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5178,6 +5186,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15684,10 +15710,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18760,6 +18782,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19151,6 +19177,53 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +msgid "Person Relationship Filter" +msgstr "Mô tả: " + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +msgid "Relationship Filter" +msgstr "Mô tả: " + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24348,25 +24421,74 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "Kiểm tra tiêu đề địa điểm " + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +#, fuzzy +#| msgid "Colors" +msgid "Hover color" +msgstr "Màu" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/zh_CN.po b/po/zh_CN.po index e5e4c07d9..d7746438a 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VERSION 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Tian Shixiong \n" "Language-Team: Chinese (Simplified Han script) Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2382,12 +2396,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "搜索" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5177,6 +5185,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15701,10 +15727,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18805,6 +18827,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "概览" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19199,6 +19225,55 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +#, fuzzy +#| msgid "Relative" +msgid "Person Relationship Filter" +msgstr "亲属 " + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +#, fuzzy +#| msgid "Relative" +msgid "Relationship Filter" +msgstr "亲属 " + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24430,28 +24505,74 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 #, fuzzy -#| msgid "Select filter to restrict people" -msgid "Select filter to restrict list" -msgstr "为限制的人员选择过滤器" +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "检查地点名称" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, fuzzy, python-format +#| msgid "Missing header" +msgid "[Missing %s]" +msgstr "缺少文件头" + +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" +msgstr "" + +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" +msgstr "" + +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" +msgstr "" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." @@ -24874,6 +24995,14 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Search" +#~ msgstr "搜索" + +#, fuzzy +#~| msgid "Select filter to restrict people" +#~ msgid "Select filter to restrict list" +#~ msgstr "为限制的人员选择过滤器" + #~ msgid "Primary Name" #~ msgstr "原名" @@ -24890,9 +25019,6 @@ msgstr "" #~ msgid "Descendant generations" #~ msgstr "后辈代数" -#~ msgid "Overview" -#~ msgstr "概览" - #, python-format #~ msgid "Cannot validate \"%(file)s\" !" #~ msgstr "无法验证 \"%(file)s\" !" diff --git a/po/zh_HK.po b/po/zh_HK.po index a3d016c69..d4b5e54a9 100644 --- a/po/zh_HK.po +++ b/po/zh_HK.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (Hong Kong) <(nothing)>\n" @@ -23,6 +23,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -714,23 +741,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "尋找地點域" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1203,6 +1213,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2355,12 +2369,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5106,6 +5114,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15585,10 +15611,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18636,6 +18658,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19017,6 +19043,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24194,25 +24265,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "檢查地點名稱" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 diff --git a/po/zh_TW.po b/po/zh_TW.po index 69ee34a51..bee8f8726 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-03 09:18-0700\n" +"POT-Creation-Date: 2026-07-29 09:58-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (traditional) \n" @@ -23,6 +23,33 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +msgid "Birthdays" +msgstr "" + +msgid "Ignore birthdays with tag" +msgstr "" + +msgid "Month and day" +msgstr "" + +msgid "Only show birthdays with tag" +msgstr "" + +msgid "Proximity to current date" +msgstr "" + +msgid "Sort birthdays by" +msgstr "" + +msgid "Sort dates of death by" +msgstr "" + +msgid "a gramplet that displays death dates in sorted order" +msgstr "" + +msgid "a gramplet that displays the birthdays of the living people" +msgstr "" + #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" msgstr "" @@ -714,23 +741,6 @@ msgstr "" msgid "Looking for children birth order" msgstr "尋找地點域" -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:25 -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:33 -msgid "Birthdays" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.gpr.py:26 -msgid "a gramplet that displays the birthdays of the living people" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:45 -msgid "Ignore birthdays with tag" -msgstr "" - -#: BirthdaysGramplet/BirthdaysGramplet.py:46 -msgid "Only show birthdays with tag" -msgstr "" - #: CalculateEstimatedDates/CalculateEstimatedDates.gpr.py:10 #: CalculateEstimatedDates/CalculateEstimatedDates.py:194 msgid "Calculate Estimated Dates" @@ -1203,6 +1213,10 @@ msgid "" "Edit -> Set Home Person." msgstr "" +#: CombinedView/personpage.py:306 CombinedView/personpage.py:368 +msgid "Add existing child to family" +msgstr "" + #: CombinedView/personpage.py:597 #, python-format msgid "%(event_type)s: %(date)s in %(place)s" @@ -2355,12 +2369,6 @@ msgstr "" msgid "the chart type runs out of bounds" msgstr "" -#: DenominoViso/DenominoViso.py:2002 GraphView/graphview.py:968 -#: NoteCleanup/NoteCleanup.py:126 SearchGramplet/SearchGramplet.gpr.py:9 -#: SearchGramplet/SearchGramplet.gpr.py:19 -msgid "Search" -msgstr "" - #: DenominoViso/DenominoViso.py:2553 msgid "DenominoViso Options" msgstr "" @@ -5106,6 +5114,24 @@ msgstr "" msgid "Allow regular expressions." msgstr "" +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 +#: ExcludeSubtreeFilter/excludesubtree.py:106 +msgid "People reachable from , stopping at matches" +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 +#: ExcludeSubtreeFilter/excludesubtree.py:109 +msgid "" +"Matches people who are reachable starting from (walking all parents " +"and children of attached families, recursively) stopping at persons in " +"." +msgstr "" + +#: ExcludeSubtreeFilter/excludesubtree.py:121 +#: FilterRules/isrelatedwithfiltermatch.py:80 +msgid "Retrieving all sub-filter matches" +msgstr "" + #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" msgstr "" @@ -15585,10 +15611,6 @@ msgstr "" msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:923 -msgid "Merge" -msgstr "" - #: GrampsWebSync/grampswebsync.py:948 msgid "Local changes" msgstr "" @@ -18636,6 +18658,10 @@ msgstr "" msgid "Gramplet showing an overview of events for a person" msgstr "" +#: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 +msgid "Overview" +msgstr "" + #: Overview/Overview.gpr.py:45 msgid "Family Overview" msgstr "" @@ -19017,6 +19043,51 @@ msgstr "" msgid "No source information found" msgstr "" +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 +msgid "Person Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 +msgid "Gramplet providing a person filter on relationships" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 +msgid "Relationship Filter" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:82 +msgid "Children of name match" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:84 +msgid "Matches children of anybody with a given name" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:251 +msgid "Sibling 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:252 +msgid "Sibling 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:253 +msgid "Child 1" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:254 +msgid "Child 2" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:282 +msgid "Probably Alive" +msgstr "" + +#: PersonRelationshipFilter/PersonRelationshipFilter.py:284 +#, python-format +msgid "example: '%(msg1)s' or '%(msg2)s'" +msgstr "" + #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 msgid "Photo Tagging" @@ -24194,25 +24265,72 @@ msgstr "" msgid "Display Icons" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:4 -#: WordleGramplet/WordleGramplet.gpr.py:10 -msgid "Wordle" +#: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 +msgid "Given Name Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:25 +msgid "Gramplet showing all given names as a word cloud" msgstr "" -#: WordleGramplet/WordleGramplet.gpr.py:13 -msgid "Gramplet used to make word clouds with wordle.net" +#: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 +msgid "Surname Word Cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:41 +msgid "Gramplet showing all surnames as a word cloud" +msgstr "" + +#: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place Word Cloud" +msgstr "檢查地點名稱" + +#: WordClouds/WordClouds.gpr.py:57 +msgid "Gramplet showing all places as a word cloud" +msgstr "" + +#: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 +#, python-format +msgid "Number of %s" +msgstr "" + +#: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 +msgid "Color (low)" +msgstr "" + +#: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 +msgid "Color (high)" +msgstr "" + +#: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 +msgid "Hover color" +msgstr "" + +#: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 +msgid "Layout quality" +msgstr "" + +#: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 +msgid "Filter missing/unknown words" +msgstr "" + +#: WordClouds/cloudgramplet.py:207 +#, python-format +msgid "[Missing %s]" msgstr "" -#: WordleGramplet/WordleGramplet.py:126 -msgid "[Missing]" +#: WordClouds/givennamewordcloudgramplet.py:50 +msgid "Click given name to view people with that given name" msgstr "" -#: WordleGramplet/WordleGramplet.py:140 -msgid "Number of font sizes" +#: WordClouds/placewordcloudgramplet.py:53 +msgid "Click place name to view references" msgstr "" -#: WordleGramplet/WordleGramplet.py:144 -msgid "Select filter to restrict list" +#: WordClouds/surnamewordcloudgramplet.py:49 +msgid "Click surname to view people with that surname" msgstr "" #: libaccess/libaccess.gpr.py:34 From c0cba45f21575f5be85238a8e252cf542849e069 Mon Sep 17 00:00:00 2001 From: Kaj Arne Mikkelsen Date: Wed, 5 Aug 2026 20:02:13 +0200 Subject: [PATCH 106/156] Translated using Weblate (Danish) Currently translated at 58.5% (3266 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ Translated using Weblate (Danish) Currently translated at 58.4% (3260 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ --- po/da.po | 144 ++++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/po/da.po b/po/da.po index 6660ad37d..5493ab282 100644 --- a/po/da.po +++ b/po/da.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-23 14:01+0000\n" +"PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish \n" @@ -29,26 +29,22 @@ msgid "Ignore birthdays with tag" msgstr "Undlad fødseldage med ettiket" msgid "Month and day" -msgstr "" +msgstr "Måned og dag" msgid "Only show birthdays with tag" msgstr "Vis kun fødseldage med ettiket" msgid "Proximity to current date" -msgstr "" +msgstr "Afstand til nuværende dato" -#, fuzzy -#| msgid "Sort by:" msgid "Sort birthdays by" -msgstr "Sortér efter:" +msgstr "Sortér fødselsdage efter" msgid "Sort dates of death by" -msgstr "" +msgstr "Sortér dødsdatoer efter" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "en gramplet der viser fødselsdage for levende persone" +msgstr "en gramplet der viser dødsdatoer i sorteret rækkefølge" msgid "a gramplet that displays the birthdays of the living people" msgstr "en gramplet der viser fødselsdage for levende persone" @@ -270,10 +266,8 @@ msgstr "" "generation." #: AncestryTableReport/AncestryTableReport.py:643 -#, fuzzy -#| msgid "The style used for the table of contents header." msgid "The style used for the number of ancestors per generation." -msgstr "Stilen der bruges til antallet af aner per generation." +msgstr "Stilen der benyttes til antallet af aner per generation." #: AncestryTableReport/AncestryTableReport.py:654 msgid "" @@ -291,28 +285,24 @@ msgid "A gramplet that displays the anniversaries of events" msgstr "En gramplet der viser årsdage for begivenheder" #: AnniversariesGramplet/AnniversariesGramplet.py:52 -#, fuzzy -#| msgid "Double-click on a row to edit the selected participant." msgid "Double-click on a row to edit the event." -msgstr "Dobbeltklik på en række for at redigere den valgte deltager." +msgstr "Dobbeltklik på en række for at redigere den valgte begivenhed." #: AnniversariesGramplet/AnniversariesGramplet.py:59 -#, fuzzy -#| msgid "Other participants" msgid "Participant" -msgstr "Andre deltagere" +msgstr "Deltager" #: ArchiveAssist/ArchiveAssist.gpr.py:22 msgid "" "Parses strings from Riksarkivet and ArkivDigital to create sources and " "citations." msgstr "" +"Behandler strenge fra Riksarkivet og ArkivDigital for at danne kilder og " +"kildehenvisninger." #: ArchiveAssist/ArchiveAssist.gpr.py:26 -#, fuzzy -#| msgid "Archive file" msgid "Archive Assist" -msgstr "Arkivfil" +msgstr "Arkivhjælp" #: AssociationsTool/associationstool.gpr.py:36 msgid "Check Associations data" @@ -1001,7 +991,7 @@ msgstr "udført!\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:432 #, python-format msgid "Skipped %d people due to errors (see log).\n" -msgstr "" +msgstr "Sprang over %d personer på grund af fejl (se log).\n" #: CalculateEstimatedDates/CalculateEstimatedDates.py:347 msgid "" @@ -1076,7 +1066,7 @@ msgstr "Tilføjet %d hændelser." #: CalculateEstimatedDates/CalculateEstimatedDates.py:562 #, python-format msgid " (Skipped %d rows due to errors; see log.)" -msgstr "" +msgstr " (Sprang over %d rækker på grund af fejl: se log.)" #: CalculateEstimatedDates/CalculateEstimatedDates.py:589 msgid "Estimated date" @@ -1125,47 +1115,51 @@ msgstr "Rediger valg" #: ChatWithTree/ChatWithTree.gpr.py:9 msgid "Chat With Tree Interactive Addon" -msgstr "" +msgstr "Chat med Tree Interactive-tilføjelsesprogrammet" #: ChatWithTree/ChatWithTree.gpr.py:10 msgid "" "Chat With Tree with the help of AI Large Language Model, needs litellm module" msgstr "" +"Chat med Træ ved hjælp af AI Large Language Model, hat behov for litellm " +"modulet" #: ChatWithTree/ChatWithTree.gpr.py:18 msgid "Chat With Tree" -msgstr "" +msgstr "Chat med træ" #: ChatWithTree/ChatWithTree.py:149 msgid "Type a message..." -msgstr "" +msgstr "Skriv en besked..." #: ChatWithTree/ChatWithTree.py:153 GrampsAssistant/grampsassistant.py:216 #: GrampsAssistant/grampsassistant.py:920 msgid "Send" -msgstr "" +msgstr "Send" #: ChatWithTree/ChatWithTree.py:163 msgid "Chat with Tree initialized. Type /help for help." -msgstr "" +msgstr "Chat med træ er påbegyndt. Tast /help for hjælp." #: ChatWithTree/ChatWithTree.py:463 msgid "" "The ChatWithTree addon is not yet initialized. Please " "reload Gramps or select a database." msgstr "" +"ChatWithTree tillægget er endnu ikke initialiseret. " +"Genstart venligst Gramps eller vælg en database." #: ChatWithTree/ChatWithTree.py:472 msgid "The chatbot is currently processing a query. Please wait." -msgstr "" +msgstr "Chatbotten behandler for øjeblikket et spørgsmål. Vent venligst." #: ChatWithTree/ChatWithTree.py:499 msgid "An error occurred while processing your query." -msgstr "" +msgstr "En fejl opstod ved behandling af dit spørgsmål." #: ChatWithTree/chatwithllm.py:119 msgid "Tree: '{}'" -msgstr "" +msgstr "Træ: '{}'" #: CheckPlaceTitles/checkplacetitles.glade:56 msgid "Remove content of place title field if it does not match" @@ -1236,6 +1230,8 @@ msgid "" "An enhanced citation formatter that adds a repository and call number to the " "standard functionality." msgstr "" +"En udvidet kildehenvisningsformatérer, der tilføjer et arkiv og " +"samtalenummer til standardfunktionen." #: CliMerge/CliMerge.gpr.py:9 msgid "Command Line Merge" @@ -1308,10 +1304,8 @@ msgstr "" "Proband." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Tilføjer mærkater til familie (%s)" +msgstr "Tilføj eksisterende barn til familien" #: CombinedView/personpage.py:597 #, python-format @@ -1363,10 +1357,9 @@ msgstr "" #: D3Charts/DescendantIndentedTree.py:1681 #: D3Charts/DescendantIndentedTree.py:1701 DenominoViso/DenominoViso.py:395 #: DenominoViso/DenominoViso.py:412 DenominoViso/DenominoViso.py:2504 -#, fuzzy, python-brace-format -#| msgid "Failure writing to %s" +#, python-brace-format msgid "Failure writing {target_path}: {message}" -msgstr "Fejl ved skrivning til %s" +msgstr "Fejl ved skrivning til: {target_path}:{message}" #: D3Charts/AncestralCollapsibleTree.py:346 D3Charts/AncestralFanChart.py:421 #: D3Charts/DescendantIndentedTree.py:1523 @@ -1507,10 +1500,9 @@ msgstr "" "Vil du forsøge at danne den?" #: D3Charts/DescendantIndentedTree.py:1379 -#, fuzzy, python-brace-format -#| msgid "Failed to create %s: %s" +#, python-brace-format msgid "Failed to create {target_path}: {message}" -msgstr "Kunne ikke danne%s: %s" +msgstr "Kunne ikke danne {target_path}:{message}" #: D3Charts/DescendantIndentedTree.py:1395 #, python-format @@ -1767,16 +1759,17 @@ msgid "The font size in pixels for biography body text." msgstr "Font størrelse i pixels for biografi body tekst." #: DEWebConnectPack/DEWebPack.gpr.py:11 +#, fuzzy msgid "DE Web Connect Pack" -msgstr "" +msgstr "DE Web Connect Pack" #: DEWebConnectPack/DEWebPack.gpr.py:12 msgid "Collection of Web sites for the DE (requires libwebconnect)" -msgstr "" +msgstr "Samling af Web steder for DE (kræver libwebconnect)" #: DEWebConnectPack/DEWebPack.py:37 msgid "Bielefeld Academic Search" -msgstr "" +msgstr "Akademisk søgning i Bielefeld" #: DEWebConnectPack/DEWebPack.py:39 SVWebconnectPack/SVWebPack.py:34 #: UKWebConnectPack/UKWebPack.py:35 USWebConnectPack/USWebPack.py:34 @@ -1785,33 +1778,33 @@ msgid "FamilySearch.org" msgstr "FamilySearch.org" #: DEWebConnectPack/DEWebPack.py:42 -#, fuzzy -#| msgid "GoogleEarth" msgid "Google Archives" -msgstr "GoogleEarth" +msgstr "Google arkiv" #: DEWebConnectPack/DEWebPack.py:43 #, fuzzy #| msgid "GoogleEarth" msgid "DE Google" -msgstr "GoogleEarth" +msgstr "DE Google" #: DEWebConnectPack/DEWebPack.py:44 UKWebConnectPack/UKWebPack.py:39 #: USWebConnectPack/USWebPack.py:42 +#, fuzzy msgid "Open Library" -msgstr "" +msgstr "Open Library" #: DEWebConnectPack/DEWebPack.py:45 msgid "Surname map (1890-1996)" -msgstr "" +msgstr "Efternavnskort (1890-1996)" #: DEWebConnectPack/DEWebPack.py:46 +#, fuzzy msgid "GenWiki" -msgstr "" +msgstr "GenWiki" #: DEWebConnectPack/DEWebPack.py:47 msgid "German digital library" -msgstr "" +msgstr "Det Tyske digitale bibliotek" #: DNA/dnasegmentmap.gpr.py:26 DNA/dnasegmentmap.gpr.py:35 msgid "DNA Segment Map" @@ -1835,13 +1828,11 @@ msgstr "Kr" #: DNA/dnasegmentmap.py:759 msgid "Legend" -msgstr "" +msgstr "Tegnforklaring" #: DNA/dnasegmentmap.py:761 -#, fuzzy -#| msgid "Grandparents" msgid ": Grandparent" -msgstr "Bedsteforældre" +msgstr ":Bedsteforælder" #: DNA/dnasegmentmap.py:1056 #, python-brace-format @@ -1860,16 +1851,16 @@ msgstr " SNPs" #: DNA/dnasegmentmap.py:1061 msgid " : Starts at " -msgstr "" +msgstr " : Begynder ved " #: DNA/dnasegmentmap.py:1063 msgid " and ends at " msgstr " og slutter ved " #: DNA/dnasegmentmap.py:1079 DNA/dnasegmentmap.py:1082 -#, python-brace-format +#, fuzzy, python-brace-format msgid "{0}" -msgstr "" +msgstr "{0}" #: DNA/dnasegmentmap.py:1086 #, python-format @@ -1877,18 +1868,18 @@ msgid "%(ancestor1)s and %(ancestor2)s" msgstr "%(ancestor1)s og %(ancestor2)s" #: DNA/dnasegmentmap.py:1090 -#, fuzzy, python-brace-format -#| msgid "Relationship Type" +#, python-brace-format msgid "" "\n" "Relationship: {0} " -msgstr "Slægtskabstype:" +msgstr "" +"\n" +"Slægtskab:{0} " #: DNA/dnasegmentmap.py:1091 -#, fuzzy, python-brace-format -#| msgid "Ancestor" +#, python-brace-format msgid " Ancestor: {0}" -msgstr "Ane" +msgstr " Ane:{0}" #: DNA/dnasegmentmap.py:1115 msgid "" @@ -2065,14 +2056,12 @@ msgstr "i" #: DataEntryGramplet/DataEntryGramplet.py:428 #: DataEntryGramplet/DataEntryGramplet.py:507 -#, fuzzy -#| msgid "Family Tree file" msgid "No Family Tree is open." -msgstr "Slægtsbog" +msgstr "Ingen slægtsbog er åben." #: DataEntryGramplet/DataEntryGramplet.py:429 msgid "Please open a Family Tree to edit data." -msgstr "" +msgstr "Åben venligst en slægtsbog for at redigere data." #: DataEntryGramplet/DataEntryGramplet.py:446 #: DataEntryGramplet/DataEntryGramplet.py:578 @@ -2082,7 +2071,7 @@ msgstr "Gramplet Data rettelse: %s" #: DataEntryGramplet/DataEntryGramplet.py:508 msgid "Please open a Family Tree before adding a person." -msgstr "" +msgstr "Åben venligst en slægtsbog før du tilføjer en person." #: DataEntryGramplet/DataEntryGramplet.py:524 msgid "Can't add new person." @@ -2174,21 +2163,19 @@ msgstr "Datoberegner" #: DateCalculator/DateCalculator.gpr.py:23 msgid "Perform date math calculations" -msgstr "" +msgstr "Udfør beregninger for datoer" #: DateCalculator/DateCalculator.py:86 msgid "Reference Date or Date Range" -msgstr "" +msgstr "Referencedato eller datointerval" #: DateCalculator/DateCalculator.py:86 -#, fuzzy -#| msgid "Invalid date " msgid "a valid Gramps date" -msgstr "Ugyldig dato " +msgstr "en gyldig Gramps dato" #: DateCalculator/DateCalculator.py:90 msgid "Date or offset ±y or ±y, m, d" -msgstr "" +msgstr "Dato eller forskydning ±y eller ±y, m, d" #: DateCalculator/DateCalculator.py:92 msgid "" @@ -2196,6 +2183,9 @@ msgid "" "2. a positive or negative number, representing years\n" "3. a positive or negative list of values, representing years, months, days" msgstr "" +"1. en Dato\n" +"2. et positivt eller negativt tal, der angiver år\n" +"3. en positiv eller negativ liste af værdier, der angiver år, måneder, dage" #: DateCalculator/DateCalculator.py:96 msgid "Result" @@ -2212,7 +2202,7 @@ msgstr "Kopier" #: DateCalculator/DateCalculator.py:159 msgid "Error: invalid date for first expression" -msgstr "" +msgstr "Fejl: ugyldig dato i det første udtryk" #: DateCalculator/DateCalculator.py:169 msgid "Error: invalid offset for second expression" From ab2e97c35a0f18c80112675fc8686bcafe0cde5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirko=20Leonh=C3=A4user?= Date: Wed, 5 Aug 2026 20:02:14 +0200 Subject: [PATCH 107/156] Translated using Weblate (German) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/de/ --- po/de.po | 122 ++++++++++++++++++++----------------------------------- 1 file changed, 44 insertions(+), 78 deletions(-) diff --git a/po/de.po b/po/de.po index 796214ec1..47182c4fa 100644 --- a/po/de.po +++ b/po/de.po @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-05 22:48+0000\n" +"PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German \n" @@ -33,7 +33,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Geburtstage" @@ -42,28 +42,22 @@ msgid "Ignore birthdays with tag" msgstr "Geburtstage mit Etikett ignorieren" msgid "Month and day" -msgstr "" +msgstr "Monat und Tag" msgid "Only show birthdays with tag" msgstr "Nur Geburtstage mit Etikett anzeigen" msgid "Proximity to current date" -msgstr "" +msgstr "Nähe zum aktuellen Datum" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Sortieren nach " +msgstr "Geburtstage sortieren nach" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Geburtsdatum des verstorbenen" +msgstr "Todesdaten sortieren nach" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "ein Gramplet, das die Geburtstage der lebenden Personen anzeigt" +msgstr "Ein Gramplet, das Sterbedaten in sortierter Reihenfolge anzeigt" msgid "a gramplet that displays the birthdays of the living people" msgstr "ein Gramplet, das die Geburtstage der lebenden Personen anzeigt" @@ -1355,10 +1349,8 @@ msgstr "" "festlegen." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Hinzufügen von Etiketten zur Familie (%s)" +msgstr "Vorhandenes Kind zur Familie hinzufügen" #: CombinedView/personpage.py:597 #, python-format @@ -5583,6 +5575,7 @@ msgstr "Reguläre Ausdrücke zulassen." #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" msgstr "" +"Personen, die über erreichbar sind, wobei als Grenze gilt" #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5591,11 +5584,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"Es werden Personen ermittelt, die ausgehend von erreichbar sind " +"(wobei alle Eltern und Kinder der zugehörigen Familien rekursiv durchlaufen " +"werden), wobei bei Personen in angehalten wird." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "Alle Übereinstimmungen der Unterfilter abrufen" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -20117,65 +20113,51 @@ msgid "No source information found" msgstr "Keine Quelleninformation gefunden" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "Beziehung zum Vater" +msgstr "Beziehungsfilter für Personen" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" +msgstr "" +"Gramplet, das einen Personenfilter für Verwandtschaftsbeziehungen " +"bereitstellt" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "Beziehung zum Vater" +msgstr "Beziehungsfilter" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "Tote Kinder" +msgstr "Kinder, deren Name übereinstimmt" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "Gibt alle Kinder einer beliebigen Person mit einem Vornamen zurück" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "Geschwister" +msgstr "Geschwister 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "Geschwister" +msgstr "Geschwister 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "Tote Kinder" +msgstr "Kind 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "Tote Kinder" +msgstr "Kind 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "Wahrscheinlich am Leben" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "Beispiel: „%(msg1)s“ oder „%(msg2)s“" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -25775,85 +25757,69 @@ msgstr "Symbole anzeigen" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "Vornamen-Wortwolke" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" +msgstr "Gramplet zeigt alle Vornamen als Wortwolke" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "Nachnamen-Wortwolke" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" +msgstr "Grammlet zeigt alle Nachnamen als Wortwolke" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "Ortsgeschichte geladen" +msgstr "Orte-Wortwolke" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "Gramplet, das Verwandte in einer Beziehung anzeigt" +msgstr "Grammlet zeigt alle Orte als Wortwolke" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "Anzahl der Seiten" +msgstr "Anzahl von %s" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "Farbig männlich" +msgstr "Farbe (niedrig)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "Farbe (hoch)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "Hair color" msgid "Hover color" -msgstr "Haarfarbe" +msgstr "Farbe bei Mauszeiger-Überfahrt" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "Layoutqualität" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "Filter für fehlende/unbekannte Wörter" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "[Fehlt]" +msgstr "[%s fehlt]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "Auf den Vornamen klicken, um Personen mit diesem Vornamen anzuzeigen" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "Bild Quellenreferenzen aufnehmen" +msgstr "Den Ortsnamen anklicken, um die Referenzen anzuzeigen" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "Auf den Nachnamen klicken, um Personen mit diesem Nachnamen anzuzeigen" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From aba53babd26b09aa3e8dbc6f66ece3795d626536 Mon Sep 17 00:00:00 2001 From: Avi Markovitz Date: Wed, 5 Aug 2026 20:02:14 +0200 Subject: [PATCH 108/156] Translated using Weblate (Hebrew) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ --- po/he.po | 162 ++++++++++++++++++++----------------------------------- 1 file changed, 59 insertions(+), 103 deletions(-) diff --git a/po/he.po b/po/he.po index d3469d48f..05a0a8ce5 100644 --- a/po/he.po +++ b/po/he.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-29 16:56+0000\n" +"PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew \n" @@ -28,28 +28,22 @@ msgid "Ignore birthdays with tag" msgstr "להתעלם מימי הולדת מתוייגים בתג" msgid "Month and day" -msgstr "" +msgstr "חודש ויום" msgid "Only show birthdays with tag" msgstr "להציג רק ימי הולדת מתוייגים בתג" msgid "Proximity to current date" -msgstr "" +msgstr "סמיכות לתריך נוכחי" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "מיון לפי " +msgstr "מיון תאריכי־לידה לפי" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "לידה תאריך של נפטר" +msgstr "מיון תאריכי־פטירה לפי" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "גרמפלט שמציג את ימי ההולדת של האנשים החיים" +msgstr "גרמפלט שמציג תאריכי־פטירה ממוינים עלפי סדר" msgid "a gramplet that displays the birthdays of the living people" msgstr "גרמפלט שמציג את ימי ההולדת של האנשים החיים" @@ -1234,7 +1228,7 @@ msgstr "לוח־גזירים אוספים" #: ClipboardGramplet/ClipboardGramplet.gpr.py:11 msgid "Gramplet for grouping collections of items to aid in data entry." -msgstr "גרמפלט לקיבוץ אוספי פריטים לסיוע בהזנת נתונים." +msgstr "גרמפלט לקיבוץ אוספי פריטים שיסיעו בהזנת נתונים." #: ClockGramplet/ClockGramplet.gpr.py:4 ClockGramplet/ClockGramplet.gpr.py:9 msgid "Clock" @@ -1288,10 +1282,8 @@ msgstr "" "האדם הרצוי כ'אדם הבית', ולאשר את הבחירה מתפריט עריכה ← קבע אדם הבית." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "הוספה תגים למשפחה (%s)" +msgstr "הוספת צאצאים קיימים למשפחה" #: CombinedView/personpage.py:597 #, python-format @@ -2602,7 +2594,7 @@ msgstr "האם לכלול הערה השייכת לעד (אם event_format מכי #: DenominoViso/DenominoViso.py:2636 msgid "Whether to include a person's attributes" -msgstr "האם לכלול תכונות אדם" +msgstr "האם לכלול מאפייני אדם" #: DenominoViso/DenominoViso.py:2640 msgid "Whether to include a person's addresses." @@ -2678,11 +2670,11 @@ msgstr "האם לכלול אזכורים לתמונות" #: DenominoViso/DenominoViso.py:2683 msgid "Source reference attribute" -msgstr "תכונות אזכורי מקור" +msgstr "מאפייני אזכורי מקור" #: DenominoViso/DenominoViso.py:2684 msgid "Image attribute that should be used as source reference" -msgstr "תכונות תמונה בהן יש להשתמש באזכור מקור" +msgstr "מאפייני תמונה בהם יש להשתמש באזכור מקור" #: DenominoViso/DenominoViso.py:2690 msgid "Style Options" @@ -5328,7 +5320,7 @@ msgstr "לאפשר ביטויים סדירים." #: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" -msgstr "" +msgstr "אנשים תואמים, שמתחילים ב־, ומסתיימים ב־" #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5337,11 +5329,13 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"התאמת אנשים שמתחילים ב־ (סריקת כל ההורים והצאצאים של משפחות מחוברות, " +"באופן רקורסיבי) ומסתימים ב־." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "אחזור כל התאמות מסננני המשנה" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -7954,7 +7948,7 @@ msgstr "קו בלוח 2" #: Form/form_ca.xml.h:541 msgid "PageRef" -msgstr "עמוד הפניה" +msgstr "עמוד אזכור" #: Form/form_ca.xml.h:542 msgid "2. Page from Schedule 1" @@ -7962,7 +7956,7 @@ msgstr "2. עמוד מלוח 1" #: Form/form_ca.xml.h:543 msgid "LineRef" -msgstr "שורה הפניה" +msgstr "שורת אזכור" #: Form/form_ca.xml.h:544 msgid "3. Line from Schedule 1" @@ -15762,7 +15756,7 @@ msgstr "דגם:" #: GrampsAssistant/grampsassistant.py:1273 msgid "Use Foundational Model" -msgstr "שימוש בדגם יסוד:" +msgstr "שימוש בדגם יסוד" #: GrampsAssistant/grampsassistant.py:1315 msgid "e.g. OPENAI_API_KEY" @@ -18047,7 +18041,7 @@ msgstr "הכללת נתונים על מדיה" #: MediaReport/media_report.py:447 msgid "Tags, notes and attributes will be included" -msgstr "תגים, הערות ותכונות יכללו בדוח" +msgstr "תגים, הערות ומאפיינים יכללו בדוח" #: MediaReport/media_report.py:450 msgid "Media width" @@ -18418,20 +18412,16 @@ msgid "Regular Expression" msgstr "ביטוי רגולרי" #: NameSuite/name_processor/views/tool_rename_tab.py:88 -#, fuzzy -#| msgid "Index of Names" msgid "Scan for Names" -msgstr "מפתח שמות" +msgstr "סריקת שמות" #: NameSuite/name_processor/views/tool_rename_tab.py:93 msgid "Preserve original name as alternative" msgstr "שימור שם מקורי כחלופה" #: NameSuite/name_processor/views/tool_rename_tab.py:160 -#, fuzzy -#| msgid "Proposed sort" msgid "Proposed" -msgstr "מיון מוצע" +msgstr "מוצע" #: NetworkChart/NetworkChart.gpr.py:24 msgid "Network Chart" @@ -19156,10 +19146,8 @@ msgid "Gramplet showing an overview of events for a person" msgstr "גרמפלט להצגת תקציר אירועים לאדם" #: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 -#, fuzzy -#| msgid "Person Overview" msgid "Overview" -msgstr "אדם סקירה" +msgstr "סקירה כללית" #: Overview/Overview.gpr.py:45 msgid "Family Overview" @@ -19558,65 +19546,49 @@ msgid "No source information found" msgstr "לא נמצא מידע מקור" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "יוחסה לאב" +msgstr "מסנן קשרי‏־קרבה לאדם" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" +msgstr "גרמפלט שמציע מסנן אדם בקשרי־קרבה" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "יוחסה לאב" +msgstr "מסנן קשר־קרבה" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "ילדים נפטר" +msgstr "צאצים מהתאמת שמות" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "התאמת צאצאים בשם מסויים של אדם כלשהו" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "אחאי" +msgstr "אחאי 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "אחאי" +msgstr "אחאי 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "ילדים נפטר" +msgstr "צאצא 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "ילדים נפטר" +msgstr "צאצא 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "כנראה בחיים" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "דוגמה: '%(msg1)s' או '%(msg2)s'" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -19987,7 +19959,7 @@ msgstr "חיפוש מקומות מקומיים ו־GeoNames להתאמה." msgid "" "Continue to merge (for local places), or to complete a place (for GeoNames)." msgstr "" -"להמשיך במיזוג (מקומות מקומיים), או להשלים תכונות מקום (מקומות GeoNames)." +"להמשיך במיזוג (מקומות מקומיים), או להשלים מאפייני מקום (מקומות GeoNames)." #: PlaceCleanup/placecleanup.glade:932 msgid "" @@ -20072,8 +20044,8 @@ msgid "" "Place Cleanup Gramplet assists in merging places, as well as completing " "places from the GeoNames web database" msgstr "" -"גרמפלט לשיקום מקומות מסייע במיזוג מקומות והשלמת תכונות מקומות מתוך מסד־נתוני " -"המרשתת של GeoNames" +"גרמפלט לשיקום מקומות מסייע במיזוג מקומות והשלמת מאפייני מקומות מתוך מסד־" +"נתוני המרשתת של GeoNames" #: PlaceCleanup/placecleanup.py:293 #, python-format @@ -20212,13 +20184,13 @@ msgid "" "parse/set the attribute fields." msgstr "" "מספק רשימה ניתנת לדפדוף של מקומות שנבחרו, ומתן אפשרות להשלים/לנתח/להגדיר את " -"שדות התכונות." +"שדות המאפיינים." #: PlaceCompletion/PlaceCompletion.py:95 msgid "" "Place Completion by parsing, file lookup and batch setting of place " "attributes" -msgstr "השלמת פרטי מקום על ידי ניתוח, חיפוש קבצים והגדרת אצווה של תכונות מקום" +msgstr "השלמת פרטי מקום על ידי ניתוח, חיפוש קבצים והגדרת אצווה של מאפייני מקום" #: PlaceCompletion/PlaceCompletion.py:258 msgid "Error in PlaceCompletion.py" @@ -22189,7 +22161,7 @@ msgstr "הסרה מאפיין סוג וערך הגדרה" #: SetAttributeTool/SetAttributeTool.py:152 msgid "Setting attributes..." -msgstr "ביצוע הגדרת תכונות..." +msgstr "מתבצעת הגדרת מאפיינים..." #: SetAttributeTool/SetAttributeTool.py:156 #, python-format @@ -22215,7 +22187,7 @@ msgstr "הסרה מאפיין" #: SetAttributeTool/SetAttributeTool.py:194 msgid "Removing attributes..." -msgstr "מתבצעת הסרת תכונות..." +msgstr "מתבצעת הסרת מאפיינים..." #: SetAttributeTool/SetAttributeTool.py:221 #, python-format @@ -24198,7 +24170,7 @@ msgstr "הערה סוג" #: ToDoReport/TodoReport.py:885 msgid "Group by reference type" -msgstr "קיבוץ לפי סוג הפניה" +msgstr "קיבוץ לפי סוג אזכור" #: ToDoReport/TodoReport.py:886 msgid "Group notes by Family, Person, Place, etc." @@ -25079,85 +25051,69 @@ msgstr "הצגת סמלים" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "ענן שמות פרטיים" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" +msgstr "גרמפלט להצגת ענן כל השמות הפרטיים" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "ענן שמות משפחה" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" +msgstr "גרמפלט להצגת ענן כל שמות המשפחה" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "היסטוריית מקום נטענה" +msgstr "ענן שמות מקום" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "גרמפלט להצגת קרובי משפחה וקשרי קירבה" +msgstr "גרמפלט להצגת ענן שמות כל המקומות" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "מספר עמודים" +msgstr "מספר %s" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "צבעוני זכר" +msgstr "צבע (נמוך)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "צבע (גבוהה)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "Hair color" msgid "Hover color" -msgstr "צבע שיער" +msgstr "צבע ריחוף" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "איכות מערך" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "מסנן מילים חסרות/לא ידועות" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "[חסר]" +msgstr "[חסר %s]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "הקשה על שם פרטי להצגת אנשים בשפ פרטי זה" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "הכללת איזכורי מקור תמונה" +msgstr "הקשה על שם מקום להצגת אזכורים" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "הקשה על שם משפחה להצגת אנשים בשם משפחה זה" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From b0c53ceeb5c54fdde98399f1e7ec0b03f511d886 Mon Sep 17 00:00:00 2001 From: Pedro Albuquerque Date: Wed, 5 Aug 2026 20:02:14 +0200 Subject: [PATCH 109/156] Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/pt_PT/ --- po/pt_PT.po | 121 +++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 78 deletions(-) diff --git a/po/pt_PT.po b/po/pt_PT.po index b2bd9cbad..faac5c6a3 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-04 17:49+0000\n" +"PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Aniversários" @@ -26,28 +26,22 @@ msgid "Ignore birthdays with tag" msgstr "Ignorar aniversários com etiqueta" msgid "Month and day" -msgstr "" +msgstr "Mês e dia" msgid "Only show birthdays with tag" msgstr "Mostrar só aniversários com etiqueta" msgid "Proximity to current date" -msgstr "" +msgstr "Proximidade à data actual" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Ordenar por " +msgstr "Ordenar aniversários por" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Data de nascimento do falecido" +msgstr "Ordenar óbitos por" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "um gramplet que mostra os aniversários de indivíduos vivos" +msgstr "um gramplet que mostra os óbitos ordenados" msgid "a gramplet that displays the birthdays of the living people" msgstr "um gramplet que mostra os aniversários de indivíduos vivos" @@ -1308,10 +1302,8 @@ msgstr "" "Editar -> Definir indivíduo inicial." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Adicionar etiquetas à família (%s)" +msgstr "Adicionar filho existente à família" #: CombinedView/personpage.py:597 #, python-format @@ -5448,6 +5440,8 @@ msgstr "Permite a utilização de expressões regulares." #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" msgstr "" +"Indivíduoss contactáveis a partir de , parando em " +"correspondências" #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5456,11 +5450,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"Compara indivíduos que estão acessíveis a partir de (percorrendo " +"recursivamente todos os pais e filhos das famílias associadas), parando nos " +"indivíduos em ." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "A obter todas as correspondências do subfiltro" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -19858,65 +19855,49 @@ msgid "No source information found" msgstr "Sem informações de fontes" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "Relação com o pai" +msgstr "Filtro de parentescos do indivíduo" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "Gramplet que mostra parentes numa relação" +msgstr "Gramplet que permite filtrar as relações por indivíduo" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "Relação com o pai" +msgstr "Filtro de parentescos" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "Filhos falecidos" +msgstr "Filhos com o mesmo nome" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "Corresponde aos filhos de qualquer pessoa com um determinado nome" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "irmão" +msgstr "Irmão 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "irmão" +msgstr "Irmão 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "Filhos falecidos" +msgstr "Filho 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "Filhos falecidos" +msgstr "Filho 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "Provavelmente vivo" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "exemplo: \"%(msg1)s\" ou \"%(msg2)s\"" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -25475,85 +25456,69 @@ msgstr "Ícones de exibição" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "Nuvem de nomes próprios" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "Gramplet que mostra parentes numa relação" +msgstr "Gramplet que mostra todos os nomes próprios numa nuvem" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "Nuvem de apelidos" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "Gramplet que mostra parentes numa relação" +msgstr "Gramplet que mostra todos os apelidos numa nuvem" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "Histórico do local carregado" +msgstr "Nuvem de locais" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "Gramplet que mostra parentes numa relação" +msgstr "Gramplet que mostra todos os locais numa nuvem" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "Número de páginas" +msgstr "Número de %s" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "De cor masculino" +msgstr "Cor (baixa)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "Cor (alta)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "Hair color" msgid "Hover color" -msgstr "Cor do cabelo" +msgstr "Cor ao pairar" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "Qualidade da disposição" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "Filtrar palavras em falta/desconhecidas" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "(em falta)" +msgstr "[%s em falta]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "Clique no nome próprio para ver indivíduos com esse nome" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "Incluir referências à fonte das imagens" +msgstr "Clique em locais para ver referências" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "Clique no qpelido para ver indivíduos com esse apelido" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From 4fb6678d7e1a9f4f11cf1ebec222b47e22a35d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Osman=20=C3=96z?= Date: Wed, 5 Aug 2026 20:02:15 +0200 Subject: [PATCH 110/156] Translated using Weblate (Turkish) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/tr/ --- po/tr.po | 192 ++++++++++++++++++++++--------------------------------- 1 file changed, 78 insertions(+), 114 deletions(-) diff --git a/po/tr.po b/po/tr.po index 72e1d9c85..8fde60e48 100644 --- a/po/tr.po +++ b/po/tr.po @@ -16,7 +16,7 @@ msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-26 17:41+0000\n" +"PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish \n" @@ -37,28 +37,22 @@ msgid "Ignore birthdays with tag" msgstr "Etiketli doğum günlerini yok say" msgid "Month and day" -msgstr "" +msgstr "Ay ve gün" msgid "Only show birthdays with tag" msgstr "Sadece etiketli doğum günlerini göster" msgid "Proximity to current date" -msgstr "" +msgstr "Geçerli tarihe yakınlık" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Şuna göre sırala " +msgstr "Doğum günlerine göre sırala" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Ölenin doğum tarihi" +msgstr "Ölüm tarihlerine göre sırala" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "Yaşayan kişilerin doğum günlerini gösteren bir gramplet" +msgstr "Ölüm tarihlerini sıralı düzende gösteren bir gramplet" msgid "a gramplet that displays the birthdays of the living people" msgstr "Yaşayan kişilerin doğum günlerini gösteren bir gramplet" @@ -1323,10 +1317,8 @@ msgstr "" "Düzenle -> Ana Kişiyi Ayarla menüsü aracılığıyla seçiminizi onaylayın." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Aileye etiket ekleme (%s)" +msgstr "Mevcut çocuğu aileye ekle" #: CombinedView/personpage.py:597 #, python-format @@ -5486,6 +5478,8 @@ msgstr "Normal ifadelerin kullanılmasına izin verin." #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" msgstr "" +" öğesinden ulaşılabilen ve noktasında duran kişilerin " +"eşleşmeleri" #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5494,11 +5488,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +" öğesinden başlayarak (bağlı ailelerin tüm ebeveynleri ve çocukları " +"boyunca özyinelemeli olarak ilerleyerek) ulaşılabilen kişileri eşleştirir; " +" içindeki kişilere ulaşıldığında durur." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "Alt filtre eşleşmelerinin tümü alınıyor" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -15125,43 +15122,44 @@ msgstr "" #: Form/form_us.xml.h:2538 msgid "What military service have you had? (Branch, Years, Nation or State)" -msgstr "" +msgstr "Hangi askerlik hizmetini yaptınız? (Branş, Yıllar, Ülke veya Eyalet)" #: Form/form_us.xml.h:2539 msgid "Do you claim exemption from draft (specify grounds)" msgstr "" +"Askerlik hizmetinden muafiyet talep ediyor musunuz (gerekçelerinizi belirtin)" #: Form/form_us.xml.h:2540 msgid "Race " -msgstr "" +msgstr "Irk " #: Form/form_us.xml.h:2542 msgid "Build / Weight" -msgstr "" +msgstr "Yapı / Ağırlık" #: Form/form_us.xml.h:2544 msgid "Hair Color" -msgstr "" +msgstr "Saç Rengi" #: Form/form_us.xml.h:2545 msgid "Bald" -msgstr "" +msgstr "Kel" #: Form/form_us.xml.h:2547 Form/form_us.xml.h:2591 msgid "Place of Registration" -msgstr "" +msgstr "Kayıt Yeri" #: Form/form_us.xml.h:2548 Form/form_us.xml.h:2590 msgid "Date of Registration" -msgstr "" +msgstr "Kayıt Tarihi" #: Form/form_us.xml.h:2549 msgid "Serial Number" -msgstr "" +msgstr "Seri Numarası" #: Form/form_us.xml.h:2550 msgid "Order Number" -msgstr "" +msgstr "Sıra Numarası" #: Form/form_us.xml.h:2551 msgid "Registration Card" @@ -15206,7 +15204,7 @@ msgstr "(1) Ad, İkinci Ad, Soyadı" #: Form/form_us.xml.h:2555 msgid "Place of Residence" -msgstr "" +msgstr "İkamet Yeri" #: Form/form_us.xml.h:2556 msgid "" @@ -15218,7 +15216,7 @@ msgstr "" #: Form/form_us.xml.h:2557 msgid "Mailing Address" -msgstr "" +msgstr "Posta Adresi" #: Form/form_us.xml.h:2558 msgid "" @@ -15230,7 +15228,7 @@ msgstr "" #: Form/form_us.xml.h:2559 msgid "Telephone" -msgstr "" +msgstr "Telefon" #: Form/form_us.xml.h:2560 msgid "" @@ -15274,7 +15272,7 @@ msgstr "" #: Form/form_us.xml.h:2569 msgid "Who will always know your address (Name & Address)" -msgstr "" +msgstr "Adresinizi her zaman bilecek kişi (Ad ve Adres)" #: Form/form_us.xml.h:2570 msgid "" @@ -15314,7 +15312,7 @@ msgstr "" #: Form/form_us.xml.h:2577 msgid "Place of employment or business" -msgstr "" +msgstr "Çalışma yeri veya işletme yeri" #: Form/form_us.xml.h:2578 msgid "" @@ -15342,7 +15340,7 @@ msgstr "" #: Form/form_us.xml.h:2583 Form/form_us.xml.h:2604 msgid "Complexion" -msgstr "" +msgstr "Ten rengi" #: Form/form_us.xml.h:2584 msgid "" @@ -15366,7 +15364,7 @@ msgstr "" #: Form/form_us.xml.h:2585 msgid "Eyes" -msgstr "" +msgstr "Gözler" #: Form/form_us.xml.h:2586 msgid "" @@ -15384,7 +15382,7 @@ msgstr "" #: Form/form_us.xml.h:2587 msgid "Hair" -msgstr "" +msgstr "Saç" #: Form/form_us.xml.h:2588 msgid "" @@ -15404,11 +15402,11 @@ msgstr "" #: Form/form_us.xml.h:2589 msgid "Other characteristics" -msgstr "" +msgstr "Diğer özellikler" #: Form/form_us.xml.h:2596 msgid "Date entered" -msgstr "" +msgstr "Girilen tarih" #: Form/form_us.xml.h:2597 msgid "Place entered" @@ -15416,55 +15414,55 @@ msgstr "Girilen yer" #: Form/form_us.xml.h:2598 msgid "Term of enlistment" -msgstr "" +msgstr "Askere yazılma süresi" #: Form/form_us.xml.h:2602 msgid "Eye color" -msgstr "" +msgstr "Göz rengi" #: Form/form_us.xml.h:2603 msgid "Hair color" -msgstr "" +msgstr "Saç rengi" #: Form/form_us.xml.h:2606 msgid "Rank in" -msgstr "" +msgstr "Sıralamada" #: Form/form_us.xml.h:2607 msgid "Branch" -msgstr "" +msgstr "Dal" #: Form/form_us.xml.h:2608 msgid "Regiment/Unit/Ship" -msgstr "" +msgstr "Alay/Birlik/Gemi" #: Form/form_us.xml.h:2610 msgid "Service Number" -msgstr "" +msgstr "Servis Numarası" #: Form/form_us.xml.h:2611 msgid "No. of enlistment" -msgstr "" +msgstr "Kayıt sayısı" #: Form/form_us.xml.h:2612 msgid "Date discharged" -msgstr "" +msgstr "Taburcu tarihi" #: Form/form_us.xml.h:2613 msgid "Place discharged" -msgstr "" +msgstr "Taburcu yeri" #: Form/form_us.xml.h:2614 msgid "Rank out" -msgstr "" +msgstr "Sıralama dışı" #: Form/form_us.xml.h:2619 msgid "Industry/Employer" -msgstr "" +msgstr "Sektör/İşveren" #: Form/form_us.xml.h:2622 msgid "Residents Status" -msgstr "" +msgstr "Sakinlerin Durumu" #: Form/formgramplet.gpr.py:31 msgid "Form Gramplet" @@ -15605,11 +15603,11 @@ msgstr "" #: GenealogyTree/treeplugins.gpr.py:31 msgid "Ancestor tree using LaTeX genealogytree" -msgstr "LaTeX genealogytree kullanarak soy ağacı" +msgstr "LaTeX genealogytree kullanarak ata ağacı" #: GenealogyTree/treeplugins.gpr.py:56 msgid "Descendant tree using LaTeX genealogytree" -msgstr "LaTeX genealogytree kullanarak soy ağacı" +msgstr "LaTeX genealogytree kullanarak soyundan gelenler ağacı" #: GenealogyTree/treeplugins.gpr.py:80 msgid "Grandparent Tree" @@ -19497,10 +19495,8 @@ msgid "Gramplet showing an overview of events for a person" msgstr "Bir kişi için olayların genel görünümünü gösteren Gramplet" #: Overview/Overview.gpr.py:37 Overview/Overview.gpr.py:53 -#, fuzzy -#| msgid "Person Overview" msgid "Overview" -msgstr "Kişi Genel Bakışı" +msgstr "Genel Bakış" #: Overview/Overview.gpr.py:45 msgid "Family Overview" @@ -19909,65 +19905,49 @@ msgid "No source information found" msgstr "Kaynak bilgisi bulunamadı" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "Baba ile ilişki" +msgstr "Kişi İlişkisi Filtresi" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "Bir ilişkide akrabaları gösteren Gramplet" +msgstr "İlişkiler üzerinde kişi filtresi sağlayan Gramplet" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "Baba ile ilişki" +msgstr "İlişki Filtresi" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "Ölen çocuklar" +msgstr "Ad eşleşmesinin çocukları" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "Belirli bir ada sahip herhangi bir kişinin çocuklarıyla eşleşir" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "kardeş" +msgstr "Kardeş 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "kardeş" +msgstr "Kardeş 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "Ölen çocuklar" +msgstr "Çocuk 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "Ölen çocuklar" +msgstr "Çocuk 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "Muhtemelen Hayatta" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "örnek: '%(msg1)s' veya '%(msg2)s'" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -25588,85 +25568,69 @@ msgstr "Simgeleri görüntüle" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "Verilen Ad Kelime Bulutu" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "Bir ilişkide akrabaları gösteren Gramplet" +msgstr "Tüm verilen adları kelime bulutu olarak gösteren Gramplet" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "Soyadı Kelime Bulutu" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "Bir ilişkide akrabaları gösteren Gramplet" +msgstr "Tüm soyadlarını bir kelime bulutu olarak gösteren Gramplet" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "Yer geçmişi yüklendi" +msgstr "Kelime Bulutu Yerleştir" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "Bir ilişkide akrabaları gösteren Gramplet" +msgstr "Tüm yerleri kelime bulutu olarak gösteren Gramplet" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "Sayfa sayısı" +msgstr "%s sayısı" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "Renkli Erkek" +msgstr "Renk (düşük)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "Renk (yüksek)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "No color" msgid "Hover color" -msgstr "Renk yok" +msgstr "Fareyle üzerine gelindiğinde renk" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "Düzen kalitesi" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "Eksik/bilinmeyen kelimeleri filtrele" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "[Eksik]" +msgstr "[Eksik %s]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "Verilen ada sahip kişileri görüntülemek için verilen ada tıklayın" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "Görüntü kaynak referanslarını dahil et" +msgstr "Referansları görüntülemek için yer adına tıklayın" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "O soyadına sahip kişileri görüntülemek için soyadına tıklayın" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From 8936b1cb2003f62c20f1ec6b637503854d803928 Mon Sep 17 00:00:00 2001 From: medardo Date: Wed, 5 Aug 2026 20:02:15 +0200 Subject: [PATCH 111/156] Translated using Weblate (Italian) Currently translated at 47.4% (2647 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/it/ --- po/it.po | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/po/it.po b/po/it.po index c6211503d..06d61d5e0 100644 --- a/po/it.po +++ b/po/it.po @@ -62,13 +62,14 @@ # Vincenzo Alfano , 2016. # Fabio Restante , 2025, 2026. # Paolo Zamponi , 2025, 2026. +# medardo , 2026. msgid "" msgstr "" "Project-Id-Version: gramps 3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-09 15:32+0000\n" -"Last-Translator: Paolo Zamponi \n" +"PO-Revision-Date: 2026-07-31 16:24+0000\n" +"Last-Translator: medardo \n" "Language-Team: Italian \n" "Language: it\n" @@ -76,7 +77,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Compleanni" @@ -85,7 +86,7 @@ msgid "Ignore birthdays with tag" msgstr "Ignora compleanni con etichetta" msgid "Month and day" -msgstr "" +msgstr "Mese e giorno" msgid "Only show birthdays with tag" msgstr "Mostra solo compleanni con etichetta" @@ -103,13 +104,11 @@ msgstr "Ordina per " msgid "Sort dates of death by" msgstr "Data di nascita del deceduto" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "un gramplet che mostra i compleanni delle persone in vita" +msgstr "Un gramplet che visualizza le date di decesso in ordine cronologico" msgid "a gramplet that displays the birthdays of the living people" -msgstr "un gramplet che mostra i compleanni delle persone in vita" +msgstr "Un gramplet che mostra i compleanni delle persone in vita" #: AllNamesQuickview/AllNames.gpr.py:4 AllNamesQuickview/AllNames.py:51 msgid "All Names of All People" From 7ce7b532ec1afc792d2b532dcbefa05a0f1be9d3 Mon Sep 17 00:00:00 2001 From: Stephan Paternotte Date: Wed, 5 Aug 2026 20:02:15 +0200 Subject: [PATCH 112/156] Translated using Weblate (Dutch) Currently translated at 100.0% (5579 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/nl/ --- po/nl.po | 122 +++++++++++++++++++------------------------------------ 1 file changed, 42 insertions(+), 80 deletions(-) diff --git a/po/nl.po b/po/nl.po index 7d02ff345..27573913d 100644 --- a/po/nl.po +++ b/po/nl.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-04 17:49+0000\n" +"PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch \n" @@ -22,7 +22,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Verjaardagen" @@ -31,29 +31,22 @@ msgid "Ignore birthdays with tag" msgstr "Verjaardagen met label negeren" msgid "Month and day" -msgstr "" +msgstr "Maand en dag" msgid "Only show birthdays with tag" msgstr "Alleen verjaardagen met label weergeven" msgid "Proximity to current date" -msgstr "" +msgstr "Nabijheid tot huidige datum" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Sorteer op " +msgstr "Verjaardagen sorteren op" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Geboortedatum overledene" +msgstr "Overlijdensdata sorteren op" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "" -"een gramplet dat de verjaardagen van de nog in leven zijnde personen toont" +msgstr "een gramplet die overlijdensdata in gesorteerde volgorde weergeeft" msgid "a gramplet that displays the birthdays of the living people" msgstr "" @@ -1336,10 +1329,8 @@ msgstr "" "bevestig uw keuze via het menu Bewerken -> Centrale persoon instellen." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Labels aan gezin (%s) toevoegen" +msgstr "Bestaand kind aan gezin toevoegen" #: CombinedView/personpage.py:597 #, python-format @@ -5541,7 +5532,7 @@ msgstr "Sta reguliere expressies toe." #: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" -msgstr "" +msgstr "Personen vanaf t/m overeenkomst " #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5550,11 +5541,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"Zoekt overeen kinderen van personen die bereikbaar zijn vanaf (alle " +"ouders en kinderen van verbonden gezinnen recursief afzoekend) tot en met " +"bij personen in ." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "Alle overeenkomsten van de subfilters aanleveren" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -20017,65 +20011,49 @@ msgid "No source information found" msgstr "Geen broninformatie gevonden" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "Verwantschap met vader" +msgstr "Relatiefilter voor personen" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "Gramplet toont verwanten in een relatie" +msgstr "Gramplet die een persoonsfilter op relaties biedt" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "Verwantschap met vader" +msgstr "Relatiefilter" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "OverledenKinderen" +msgstr "Kinderen van wie de naam overeenkomt" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "Levert de kinderen met de voornaam van personen" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "broer/zus" +msgstr "Broer/zus 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "broer/zus" +msgstr "Broer/zus 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "OverledenKinderen" +msgstr "Kind 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "OverledenKinderen" +msgstr "Kind 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "Waarschijnlijk levend" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "voorbeeld: '%(msg1)s' of '%(msg2)s'" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -25662,85 +25640,69 @@ msgstr "Pictogrammen weergeven" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "Voornamen-woordenwolk" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "Gramplet toont verwanten in een relatie" +msgstr "Gramplet die alle voornamen als woordenwolk weergeeft" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "Achternamen-woordenwolk" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "Gramplet toont verwanten in een relatie" +msgstr "Gramplet die alle achternamen als woordenwolk weergeeft" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "Plaatsgeschiedenis geladen" +msgstr "Plaatsen-woordenwolk" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "Gramplet toont verwanten in een relatie" +msgstr "Gramplet die alle plaatsen als woordenwolk weergeeft" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "Aantal pagina's" +msgstr "Aantal %s" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "Gekleurde man" +msgstr "Kleur (laag)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "Kleur (hoog)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "Hair color" msgid "Hover color" -msgstr "Haarkleur" +msgstr "Kleur bij muis-over" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "Layoutkwaliteit" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "Ontbrekende/onbekende woorden filteren" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "[Ontbreekt]" +msgstr "[Ontbrekend %s]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "Klik op een voornaam om personen met die voornaam te bekijken" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "Voeg bronvermeldingen voor afbeeldingen toe" +msgstr "Klik op een plaatsnaam om verwijzingen te bekijken" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "Klik op en achternaam om personen met die achternaam te bekijken" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From 612a1622e6ebf7377b950206a36da65e0a60af01 Mon Sep 17 00:00:00 2001 From: Milan Date: Wed, 5 Aug 2026 20:02:15 +0200 Subject: [PATCH 113/156] Translated using Weblate (Slovak) Currently translated at 98.7% (5507 of 5579 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sk/ --- po/sk.po | 121 +++++++++++++++++++------------------------------------ 1 file changed, 42 insertions(+), 79 deletions(-) diff --git a/po/sk.po b/po/sk.po index 75a112379..a5cce89d3 100644 --- a/po/sk.po +++ b/po/sk.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-29 09:58-0700\n" -"PO-Revision-Date: 2026-07-09 15:32+0000\n" +"PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak \n" @@ -21,7 +21,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Narodeniny" @@ -30,28 +30,22 @@ msgid "Ignore birthdays with tag" msgstr "Ignorovať dni narodenia so štítkom" msgid "Month and day" -msgstr "" +msgstr "Mesiac a deň" msgid "Only show birthdays with tag" msgstr "Zobraziť iba dni narodenia so štítkom" msgid "Proximity to current date" -msgstr "" +msgstr "Blízkosť k aktuálnemu dátumu" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Zoradiť podľa " +msgstr "Zoradiť narodeniny podľa" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Dátum narodenia zosnulého" +msgstr "Zoradiť dátumy úmrtia podľa" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "Gramplet, ktorý zobrazuje narodeniny žijúcich ľudí" +msgstr "Gramplet, ktorý zobrazuje dátumy úmrtia v zoradenom poradí" msgid "a gramplet that displays the birthdays of the living people" msgstr "Gramplet, ktorý zobrazuje narodeniny žijúcich ľudí" @@ -1316,10 +1310,8 @@ msgstr "" "svoj výber cez menu Upraviť -> Nastaviť domovskú osobu." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Pridávanie štítkov k rodine (%s)" +msgstr "Pridať existujúce dieťa k rodine" #: CombinedView/personpage.py:597 #, python-format @@ -5444,7 +5436,7 @@ msgstr "Povoliť regulárne výrazy." #: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" -msgstr "" +msgstr "Ľudia dostupní od , s ukončením pri zhodách vo " #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5453,11 +5445,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"Zodpovedá ľuďom, ktorí sú dostupní počnúc od (rekurzívne " +"prechádzajúc všetkých rodičov a deti pripojených rodín), s ukončením pri " +"osobách vo ." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "Získavanie všetkých podfiltrových zhôd" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -19846,65 +19841,49 @@ msgid "No source information found" msgstr "Neboli nájdené žiadne informácie o zdroji" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:25 -#, fuzzy -#| msgid "Relationship to Father" msgid "Person Relationship Filter" -msgstr "Vzťah k otcovi" +msgstr "Filter vzťahov osôb" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:26 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet providing a person filter on relationships" -msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" +msgstr "Gramplet poskytujúci filter osôb podľa vzťahov" #: PersonRelationshipFilter/PersonRelationshipFilter.gpr.py:33 -#, fuzzy -#| msgid "Relationship to Father" msgid "Relationship Filter" -msgstr "Vzťah k otcovi" +msgstr "Filter vzťahov" #: PersonRelationshipFilter/PersonRelationshipFilter.py:82 -#, fuzzy -#| msgid "Children Dead" msgid "Children of name match" -msgstr "Deti zomreté" +msgstr "Deti so zhodným menom" #: PersonRelationshipFilter/PersonRelationshipFilter.py:84 msgid "Matches children of anybody with a given name" -msgstr "" +msgstr "Zodpovedá deťom akejkoľvek osoby s daným menom" #: PersonRelationshipFilter/PersonRelationshipFilter.py:251 -#, fuzzy -#| msgid "sibling" msgid "Sibling 1" -msgstr "súrodenec" +msgstr "Súrodenec 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:252 -#, fuzzy -#| msgid "sibling" msgid "Sibling 2" -msgstr "súrodenec" +msgstr "Súrodenec 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:253 -#, fuzzy -#| msgid "Children Dead" msgid "Child 1" -msgstr "Deti zomreté" +msgstr "Dieťa 1" #: PersonRelationshipFilter/PersonRelationshipFilter.py:254 -#, fuzzy -#| msgid "Children Dead" msgid "Child 2" -msgstr "Deti zomreté" +msgstr "Dieťa 2" #: PersonRelationshipFilter/PersonRelationshipFilter.py:282 msgid "Probably Alive" -msgstr "" +msgstr "Pravdepodobne nažive" #: PersonRelationshipFilter/PersonRelationshipFilter.py:284 #, python-format msgid "example: '%(msg1)s' or '%(msg2)s'" -msgstr "" +msgstr "príklad: '%(msg1)s' alebo '%(msg2)s'" #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:29 #: PhotoTaggingGramplet/PhotoTaggingGramplet.gpr.py:38 @@ -25493,85 +25472,69 @@ msgstr "Zobraziť ikony" #: WordClouds/WordClouds.gpr.py:24 WordClouds/WordClouds.gpr.py:32 msgid "Given Name Word Cloud" -msgstr "" +msgstr "Oblak slov s danými (krstnými) menami" #: WordClouds/WordClouds.gpr.py:25 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all given names as a word cloud" -msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" +msgstr "Gramplet zobrazujúci všetky dané (krstné) mená ako oblak slov" #: WordClouds/WordClouds.gpr.py:40 WordClouds/WordClouds.gpr.py:48 msgid "Surname Word Cloud" -msgstr "" +msgstr "Oblak slov s priezviskami" #: WordClouds/WordClouds.gpr.py:41 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all surnames as a word cloud" -msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" +msgstr "Gramplet zobrazujúci všetky priezviská ako oblak slov" #: WordClouds/WordClouds.gpr.py:56 WordClouds/WordClouds.gpr.py:64 -#, fuzzy -#| msgid "Place history loaded" msgid "Place Word Cloud" -msgstr "História miesta načítaná" +msgstr "Oblak slov s miestami" #: WordClouds/WordClouds.gpr.py:57 -#, fuzzy -#| msgid "Gramplet showing relatives in a relation" msgid "Gramplet showing all places as a word cloud" -msgstr "Gramplet zobrazujúci príbuzných vo vzťahu" +msgstr "Gramplet zobrazujúci všetky miesta ako oblak slov" #: WordClouds/cloudgramplet.py:147 WordClouds/cloudgramplet.py:225 -#, fuzzy, python-format -#| msgid "Number of pages" +#, python-format msgid "Number of %s" -msgstr "Počet stránok" +msgstr "Počet od %s" #: WordClouds/cloudgramplet.py:149 WordClouds/cloudgramplet.py:227 -#, fuzzy -#| msgid "Colored Male" msgid "Color (low)" -msgstr "Farebný muž" +msgstr "Farba (slabá)" #: WordClouds/cloudgramplet.py:150 WordClouds/cloudgramplet.py:228 msgid "Color (high)" -msgstr "" +msgstr "Farba (silná)" #: WordClouds/cloudgramplet.py:151 WordClouds/cloudgramplet.py:229 -#, fuzzy -#| msgid "Hair color" msgid "Hover color" -msgstr "Farba vlasov" +msgstr "Farba pri prechode kurzorom" #: WordClouds/cloudgramplet.py:152 WordClouds/cloudgramplet.py:230 msgid "Layout quality" -msgstr "" +msgstr "Kvalita rozloženia" #: WordClouds/cloudgramplet.py:154 WordClouds/cloudgramplet.py:232 msgid "Filter missing/unknown words" -msgstr "" +msgstr "Filtrovať chýbajúce/neznáme slová" #: WordClouds/cloudgramplet.py:207 -#, fuzzy, python-format -#| msgid "[Missing]" +#, python-format msgid "[Missing %s]" -msgstr "Chýbajúci]" +msgstr "[Chýbajúce %s]" #: WordClouds/givennamewordcloudgramplet.py:50 msgid "Click given name to view people with that given name" -msgstr "" +msgstr "Kliknutím na dané (krstné) meno zobrazíte ľudí s týmto daným menom" #: WordClouds/placewordcloudgramplet.py:53 -#, fuzzy -#| msgid "Include Image source references" msgid "Click place name to view references" -msgstr "Zahrnúť zdrojové referencie obrázkov" +msgstr "Kliknutím na názov miesta zobrazíte odkazy" #: WordClouds/surnamewordcloudgramplet.py:49 msgid "Click surname to view people with that surname" -msgstr "" +msgstr "Kliknutím na priezvisko zobrazíte ľudí s týmto priezviskom" #: libaccess/libaccess.gpr.py:34 msgid "Provides a library for generic access to the database and gen.lib." From f2dd7afbb61abfed1d7c8a07a349cab32fd8b734 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Aug 2026 20:02:17 +0200 Subject: [PATCH 114/156] Update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/ --- po/ar.po | 426 +++++++++++++++++++++++------------------- po/bg.po | 434 ++++++++++++++++++++++++------------------- po/ca.po | 442 +++++++++++++++++++++++++------------------- po/cs.po | 454 ++++++++++++++++++++++++++------------------- po/cy.po | 466 +++++++++++++++++++++++++++------------------- po/da.po | 498 ++++++++++++++++++++++++++++--------------------- po/de.po | 500 +++++++++++++++++++++++++++++-------------------- po/el.po | 434 ++++++++++++++++++++++++------------------- po/en_GB.po | 434 ++++++++++++++++++++++++------------------- po/eo.po | 434 ++++++++++++++++++++++++------------------- po/es.po | 506 +++++++++++++++++++++++++++++--------------------- po/fi.po | 492 +++++++++++++++++++++++++++++-------------------- po/fr.po | 508 +++++++++++++++++++++++++++++--------------------- po/he.po | 510 ++++++++++++++++++++++++++++++-------------------- po/hr.po | 514 ++++++++++++++++++++++++++++++--------------------- po/hu.po | 438 ++++++++++++++++++++++++------------------- po/is.po | 434 ++++++++++++++++++++++++------------------- po/it.po | 498 +++++++++++++++++++++++++++++-------------------- po/ja.po | 439 ++++++++++++++++++++++++------------------- po/ka.po | 434 ++++++++++++++++++++++++------------------- po/ln.po | 434 ++++++++++++++++++++++++------------------- po/lt.po | 452 ++++++++++++++++++++++++++------------------- po/lv.po | 442 +++++++++++++++++++++++++------------------- po/mn.po | 434 ++++++++++++++++++++++++------------------- po/nb.po | 446 +++++++++++++++++++++++++------------------- po/ne.po | 434 ++++++++++++++++++++++++------------------- po/nl.po | 498 +++++++++++++++++++++++++++++-------------------- po/nn.po | 434 ++++++++++++++++++++++++------------------- po/oc.po | 434 ++++++++++++++++++++++++------------------- po/pl.po | 521 +++++++++++++++++++++++++++++++--------------------- po/pt_BR.po | 434 ++++++++++++++++++++++++------------------- po/pt_PT.po | 500 +++++++++++++++++++++++++++++-------------------- po/ru.po | 481 ++++++++++++++++++++++++++++-------------------- po/sk.po | 506 ++++++++++++++++++++++++++++++-------------------- po/sl.po | 450 ++++++++++++++++++++++++++------------------- po/sq.po | 434 ++++++++++++++++++++++++------------------- po/sr.po | 442 +++++++++++++++++++++++++------------------- po/sv.po | 497 +++++++++++++++++++++++++++++-------------------- po/tr.po | 486 ++++++++++++++++++++++++++++-------------------- po/uk.po | 514 ++++++++++++++++++++++++++++++--------------------- po/vi.po | 426 +++++++++++++++++++++++------------------- po/zh_CN.po | 426 +++++++++++++++++++++++------------------- po/zh_HK.po | 426 +++++++++++++++++++++++------------------- po/zh_TW.po | 426 +++++++++++++++++++++++------------------- 44 files changed, 11801 insertions(+), 8471 deletions(-) diff --git a/po/ar.po b/po/ar.po index 0ff18841e..fdee3505a 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps-4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2014-06-29 15:50+0300\n" "Last-Translator: Munzir Taha (منذر طه) \n" "Language-Team: Arabic <>\n" @@ -15437,8 +15437,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15446,140 +15446,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 -#, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15595,80 +15570,158 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 #, python-format -msgid "Downloading %s media file(s)" +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:920 +#, python-format +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:999 +#, python-format +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:1011 #, python-format -msgid "Uploading %s media file(s)" +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16418,11 +16471,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20524,9 +20572,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20549,9 +20597,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20560,10 +20608,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20583,178 +20631,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20792,19 +20848,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/bg.po b/po/bg.po index 007e31486..1dec47eeb 100644 --- a/po/bg.po +++ b/po/bg.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-03-14 05:09+0000\n" "Last-Translator: Iskren Petkov \n" "Language-Team: Bulgarian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21000,19 +21064,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/ca.po b/po/ca.po index 5ce351115..ead8c7f1b 100644 --- a/po/ca.po +++ b/po/ca.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: ca\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-09-03 03:01+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Catalan " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21470,19 +21534,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/cs.po b/po/cs.po index 149a91ac9..f7f37e60e 100644 --- a/po/cs.po +++ b/po/cs.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.2.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-29 14:56+0000\n" "Last-Translator: Milan \n" "Language-Team: Czech " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21528,19 +21600,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/cy.po b/po/cy.po index a392bea4f..b3a87decc 100644 --- a/po/cy.po +++ b/po/cy.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -15425,8 +15425,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15434,140 +15434,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15583,80 +15558,198 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16406,11 +16499,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20506,9 +20594,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20531,9 +20619,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20542,10 +20630,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20565,178 +20653,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20774,19 +20870,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/da.po b/po/da.po index 5493ab282..279e15265 100644 --- a/po/da.po +++ b/po/da.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish " @@ -21614,49 +21677,49 @@ msgstr "" " • language-pack-gnome-xx (Manual kontrol se instruktions link) for dit " "sprog" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " eller nyere installeret.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " eller nyere.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz ikke i system PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript ikke i system PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Godkendt: version 0.5.x er installeret.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Kræver version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig ikke fundet, (Kræver version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig installeret, version er ikke tilgængelig" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " eller nyere installeret.) (enchant module: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21665,7 +21728,7 @@ msgstr "" " • rcs %s TBD (Godkendt: version %s eller nyere installeret.hvis ikke på " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21674,20 +21737,20 @@ msgstr "" " • rcs %s TBD (Kræver version %s eller nyere installeret. hvis ikke på " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 library : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "fandt en anden font" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21695,81 +21758,81 @@ msgstr "" "For tillæg Networkchart, font White Rabbit giver et virkeligt læsbart resultat.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "ikke installeret" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " og en af disse: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") eller (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Installeret(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Installeret(Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "ikke installeret " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "ikke fundet." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Standard. Godkendt: program installeret - 32bit på 64bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Kræver gramps tillæg listet under 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF installeret" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Kræver version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " installeret.)Godkendt: version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV ansigtsgenkendelse: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: ikke fundet. Kræver version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Kræver: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Operativ System: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Locale Settings:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "ikke sat" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "ikke testet" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21816,7 +21879,7 @@ msgstr "" "ordbøger)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21824,13 +21887,13 @@ msgstr "" "\n" "Gramps miljøvariabler\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "fundet" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26202,6 +26265,29 @@ msgstr "" " \"%s\"\n" " i din foretrukne web navigator ..." +#~ msgid "Error accessing media object." +#~ msgstr "Fejl ved tilgang til medieobjekt." + +#~ msgid "Server authorization error." +#~ msgstr "Server godkendelsesfejl." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Server godkendelsesfejl: ikke tilstrækkelige tilladelser." + +#~ msgid "Error: URL not found." +#~ msgstr "Fejl: Kan ikke finde URL." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Fejl %s ved forbindelse til server." + +#, fuzzy +#~| msgid "" +#~| "Unable to synchronize changes to server: objects have been modified." +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "" +#~ "Kan ikke synkronisere ændringer til serveres: objekter er blevet ændret." + #~ msgid "Search" #~ msgstr "Søg" diff --git a/po/de.po b/po/de.po index 47182c4fa..9ec1fb9fc 100644 --- a/po/de.po +++ b/po/de.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German " @@ -21856,49 +21930,49 @@ msgstr "" " • language-pack-gnome-xx (manuelle Prüfung siehe Anleitungslink) für deine " "Sprache " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " oder höher installiert.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " oder höher)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz nicht im Systempfad (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript nicht im Systempfad (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Bestanden: Version 0.5.x ist installiert.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Benötigt Version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig nicht gefunden, (Benötigt Version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig installiert, Version nicht verfügbar" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " oder höher installiert.) (enchant Modul: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21907,7 +21981,7 @@ msgstr "" " • rcs %s TBD (Bestanden: Version %s oder höher installiert. Wenn nicht " "unter Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21916,22 +21990,22 @@ msgstr "" " • rcs %s TBD (Erfordert die Installation von Version %s oder höher. Wenn " "nicht unter Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" "Die ausführbare Datei „exiv2“ ist nicht installiert, die Version von " "„libexiv2“ kann nicht ermittelt werden." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2-Bibliothek: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "eine andere Schriftart gefunden" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21940,81 +22014,81 @@ msgstr "" "href=\"https://www.fontsquirrel.com/fonts/white-rabbit\">White Rabbit " "ein äußerst lesbares Ergebnis.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "nicht installiert" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " und einer der folgenden Optionen: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") oder (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Installiert(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Installiert(Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "nicht installiert " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "nicht gefunden." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Standard. Bestanden: Programm installiert - 32Bit auf 64Bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Benötigt die unter 'Plugin lib' aufgeführte Gramps-Erweiterung)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF installiert" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".) (Benötigt Version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " installiert.) (Bestanden: Version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV-Gesichtserkennung: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: nicht gefunden. Benötigt Version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Benötigt: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Betriebssystem: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Gebietsschemaeinstellungen:" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "nicht gesetzt" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "nicht getestet" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -22061,7 +22135,7 @@ msgstr "" "Übersetzungen und Wörterbücher ausgewählt sind)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -22069,13 +22143,13 @@ msgstr "" "\n" "Gramps Umgebungsvariablen:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "gefunden" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26273,6 +26347,28 @@ msgstr "" " „%s“\n" " in deinem bevorzugten Web- Browser zu öffnen ..." +#~ msgid "Error accessing media object." +#~ msgstr "Fehler beim Zugriff auf das Medienobjekt." + +#~ msgid "Server authorization error." +#~ msgstr "Server-Autorisierungsfehler." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Server-Autorisierungsfehler: unzureichende Berechtigungen." + +#~ msgid "Error: URL not found." +#~ msgstr "Fehler: URL nicht gefunden." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Fehler %s beim Herstellen der Verbindung zum Server." + +#~ msgid "URL error while connecting to server." +#~ msgstr "URL-Fehler bei der Verbindung zum Server." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Änderungen können nicht mit dem Server synchronisiert werden." + #~ msgid "Search" #~ msgstr "Suche" diff --git a/po/el.po b/po/el.po index d928985a5..ef5e3deec 100644 --- a/po/el.po +++ b/po/el.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.0.3.\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-12-23 02:37+0000\n" "Last-Translator: klak kloyk \n" "Language-Team: Greek " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20790,19 +20854,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/en_GB.po b/po/en_GB.po index cf5d283f5..ec4eff8f2 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -28,7 +28,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.5.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-11 13:01+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom) " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21000,19 +21064,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/eo.po b/po/eo.po index 2e4680c04..0dc8a6cb3 100644 --- a/po/eo.po +++ b/po/eo.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-08-29 20:30+0000\n" "Last-Translator: jmichault \n" "Language-Team: Esperanto " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20829,19 +20893,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/es.po b/po/es.po index 88a083e8c..3296fc2a5 100644 --- a/po/es.po +++ b/po/es.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-24 05:14+0000\n" "Last-Translator: Juan Saavedra \n" "Language-Team: Spanish " @@ -21761,49 +21827,49 @@ msgstr "" " • language-pack-gnome-xx (Compruebe instrucciones del enlace al manual) " "para su Idioma " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " o mayor instalado.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " o mayor)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz no en la ruta PATH del sistema" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript no dentro de la ruta PATH del sistema" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Aprobado: está instalada la versión 0.5.x.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Requiere versión 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig no encontrado, (Requires version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig instalado, versión no disponible" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " o más alto instalado.) (embelesar módulo: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21812,7 +21878,7 @@ msgstr "" " • rcs %s TBD (Aprobado: versión %s o más sumo instalado. Si no encima " "Windows de Microsoft)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21821,20 +21887,20 @@ msgstr "" " • rcs %s TBD (Requiere versión %s o mayor instalado. Si no en Microsoft " "Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 biblioteca : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "encontrada otro letra tipográfica" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21843,81 +21909,81 @@ msgstr "" "fonts/white-rabbit\">Conejo Blanco proporciona un resultado " "extremadamente legible.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "no instalado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " y uno de los dos: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") o (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Instalado(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Instalado(Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "no instalado " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "no encontrado." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Estándar. Pasado: programa instalado - 32-bit en 64-bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Requiere el complemento de gramps enumerado bajo 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF instalado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Requiere versión " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " instalado.)(Aprobado: versión " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV detección de rostro: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: no encontrado. Requiere versión " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Requiere: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Sistema operativo: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Locale Settings:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "no fijado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "no probado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21964,7 +22030,7 @@ msgstr "" "diccionarios)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21972,13 +22038,13 @@ msgstr "" "\n" "Variables de entorno de Gramps:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "encontrado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26235,6 +26301,28 @@ msgstr "" " «%s»\n" " en su navegador web preferido…" +#~ msgid "Error accessing media object." +#~ msgstr "Error al acceder objeto del medio." + +#~ msgid "Server authorization error." +#~ msgstr "Error autorizativo del servidor." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Error autorizativo del servidor: permisos insuficientes." + +#~ msgid "Error: URL not found." +#~ msgstr "Error: URL no encontrado." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Error %s mientras se conectaba al servidor." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Error URL mientras se conectaba al servidor." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "No es capaz de sincronizar cambios al servidor." + #~ msgid "Search" #~ msgstr "Búsqueda" diff --git a/po/fi.po b/po/fi.po index 865e9d7d6..baf8f44c5 100644 --- a/po/fi.po +++ b/po/fi.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: fi\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-26 17:40+0000\n" "Last-Translator: Juha Mäkeläinen \n" "Language-Team: Finnish " @@ -21511,49 +21583,49 @@ msgstr "" " • language-pack-gnome-xx (tarkasta, katso ohjelinkki) kielellesi " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " tai uudempi asennettuna.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " tai suurempi)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz puuttuu järjestelmän PATH:ista" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript puuttuu järjestelmän PATH:ista" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Hyväksytty, versio 0.5.x on asennettu.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Vaatii version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig -tiedostoa ei löydy, (Vaatii version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig asennettu, versiotietoa ei ole saatavilla" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " tai uudempi asennettuna.) (parannusmoduuli: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21562,7 +21634,7 @@ msgstr "" " • rcs %s TBD (Hyväksytään, jos versio %s tai uudempi on asennettuna tai jos " "sisältyy Microsoft Windowsiin)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21571,20 +21643,20 @@ msgstr "" " • rcs %s TBD (Vaatii version %s tai uudemman, ellei jo sisälly Microsoft " "Windowsiin)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2-kirjasto : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "löytyi toinen fontti" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21592,82 +21664,82 @@ msgstr "" "Networkchart-lisäosalle White Rabbit -fontti tarjoaa erittäin luettavan tuloksen.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "ei asennettu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " ja yksi seuraavista: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") tai (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Asennettu (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Asennettu(Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "ei asennettu " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "ei löytynyt." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" "Vakio. Läpäisty, ohjelma asennettu - 32-bittinen 64-bittisessä Windowsissa." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Vaatii 'Plugin lib' -kohdassa luetellun Gramps-lisäosan)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF-kirjasto asennettu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Vaatii version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " asennettu.)(Hyväksytty versio " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV kasvojen tunnistus: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: ei löydy. Vaatii version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Vaaditaan MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Käyttöjärjestelmä: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Kieliasetukset:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "ei asetettu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "ei testattu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21713,7 +21785,7 @@ msgstr "" "Gramps uudelleen ja muista valita kaikki käännökset ja sanakirjat)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21721,13 +21793,13 @@ msgstr "" "\n" "Gramps-ympäristömuuttujat:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "löytyi" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25972,6 +26044,28 @@ msgstr "" " \"%s\"\n" " suosikkiselaimessasi ..." +#~ msgid "Error accessing media object." +#~ msgstr "Mediaobjektin lukeminen ei onnistunut." + +#~ msgid "Server authorization error." +#~ msgstr "Palvelimen valtuutusvirhe." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Palvelimen valtuutusvirhe: riittämättömät käyttöoikeudet." + +#~ msgid "Error: URL not found." +#~ msgstr "Virhe: URL-osoitetta ei löytynyt." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Virhe %s yhdistettäessä palvelimeen." + +#~ msgid "URL error while connecting to server." +#~ msgstr "URL-virhe palvelimelle yhdistettäessä." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Muutosten synkronointi palvelimelle ei onnistunut." + #~ msgid "Search" #~ msgstr "Hae" diff --git a/po/fr.po b/po/fr.po index 0dfaf2199..d3d698f7a 100644 --- a/po/fr.po +++ b/po/fr.po @@ -39,7 +39,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-01 15:01+0000\n" "Last-Translator: \"David D.\" \n" "Language-Team: French " @@ -21902,49 +21968,49 @@ msgstr "" " • language-pack-gnome-xx (Vérification manuelle, voyez le lien vers les " "instructions) pour votre Langue " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " ou supérieur installé.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " ou plus)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz pas dans le PATH système" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript pas dans le PATH système" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Succès : la version 0.5.x est installée.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Nécessite la version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig pas trouvé, (version 0.5.x nécessaire)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig installé, version pas disponible" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " ou plus installé.) (enchant module : " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21953,7 +22019,7 @@ msgstr "" " • rcs %s à déterminer (Succès : version %s ou plus installée. Si pas sur " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21962,20 +22028,20 @@ msgstr "" " • rcs %s à déterminer (Nécessite version %s ou plus installée. Si pas sur " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (bibliothèque Exiv2 : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "une autre police a été trouvée" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21984,82 +22050,82 @@ msgstr "" "www.fontsquirrel.com/fonts/white-rabbit\">White Rabbit donne un résultat " "extrêmement lisible.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "pas installé" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " et l'un des deux : (pydotplus : " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") ou (pygraphviz : " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Installé(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Installé (Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "pas installé " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "pas trouvé." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Standard. Passé : programme installé - sur OS Win 32bit sur 64bit." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" ") ( Nécessite le greffon gramps listé sous 'Bibliothèque d'extensions')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF Installé" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Nécessite la version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " installée.)(Succès : version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(détection de visage OpenCV : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml : pas trouvé. Version requise " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Nécessite : MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Système d’exploitation : %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Paramètres " "régionaux :\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "pas défini" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "pas testé" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -22106,7 +22172,7 @@ msgstr "" "toutes les traductions et tous les dictionnaires)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -22114,13 +22180,13 @@ msgstr "" "\n" "Variables d'environnement Gramps :\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "trouvé" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26354,6 +26420,28 @@ msgstr "" " \"%s\"\n" " dans votre navigateur internet préféré ..." +#~ msgid "Error accessing media object." +#~ msgstr "Erreur en accédant à un objet medium." + +#~ msgid "Server authorization error." +#~ msgstr "Erreur d'autorisation du serveur." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Erreur d'autorisation du serveur : autorisations insuffisantes." + +#~ msgid "Error: URL not found." +#~ msgstr "Erreur : URL non trouvée." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Erreur %s en se connectant au serveur." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Erreur d'URL en se connectant au serveur." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Impossible de synchroniser les changements au serveur." + #~ msgid "Search" #~ msgstr "Rechercher" diff --git a/po/he.po b/po/he.po index 05a0a8ce5..5fbcb4218 100644 --- a/po/he.po +++ b/po/he.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew " @@ -21223,49 +21313,49 @@ msgstr "" " • language-pack-gnome-xx (בדיקה ידנית – קישור להוראות) עבור השפה " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " או מאוחרת יותר מותקנת.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " או מאוחר יותר)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz לא כלול בנתיב המערכת" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript אינו נמצא בנתיב המערכת" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (עבר: גרסה 0.5.x מותקנת.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (נדרשת גרסה 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " python-fontconfig לא נמצא, (נדרשת גרסה 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig מותקן, הגרסה אינה זמינה" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " או גרסה חדשה יותר מותקנת.) (פרקן enchant: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21273,27 +21363,27 @@ msgid "" msgstr "" " • rcs %s TBD (עבר: גרסה %s או חדשה יותר מותקנת. אם לא על Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr " • rcs %s TBD (נדרשת גרסה %s או חדשה יותר. אם לא על Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "קובץ ההרצה exiv2 אינו מותקן; לא ניתן לקבל את גרסת libexiv2." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2: %s (ספריית Exiv2: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "נמצא גופן אחר" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21301,81 +21391,81 @@ msgstr "" "לתוסף Networkchart, גופן ארנב לבן מספק תוצאה קריאה להפליא.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "לא מותקן" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " ואחת משתי האפשרויות: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") או (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "מותקן (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "מותקן (לינוקס/מק)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "לא מותקן " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "לא נמצא." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "תקני. עבר: תוכנה מותקנת – 32 ביט, על מערכת הפעלה וינדוס 64 ביט." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (נדרש תוסף גרמפס שמופיע ברשימה 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF מותקן" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(נדרשת גרסה " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " מותקן.)(עבר: גרסה " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(זיהוי פנים ב־OpenCV: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: לא נמצא. נדרשת גרסה " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • דורש: MongoDB TBD/pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • מערכת הפעלה: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "הגדרות המקמה:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "לא מוגדר" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "לא נבדק" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21420,7 +21510,7 @@ msgstr "" "גרמפס שוב ולוודא שכול התרגומים והמילונים שברשימה, נבחרו)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21428,13 +21518,13 @@ msgstr "" "\n" "משתני סביבת־עבודה גרמפס:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "נמצא" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25573,6 +25663,28 @@ msgstr "" " '%s'\n" " בדפדפן המרשתת המועדף..." +#~ msgid "Error accessing media object." +#~ msgstr "שגיאה גישה מדיה עצם." + +#~ msgid "Server authorization error." +#~ msgstr "שגיאת הרשאת שרת." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "שגיאת הרשאת שרת: הרשאות בלתי מספיקות." + +#~ msgid "Error: URL not found." +#~ msgstr "שגיאה: לא נמצא URL." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "שגיאה %s בעת התחברות לשרת." + +#~ msgid "URL error while connecting to server." +#~ msgstr "שגיאת URL בעת חיבור לשרת." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "לא ניתן לסנכרון שינויים לשרת." + #~ msgid "Search" #~ msgstr "חיפוש" diff --git a/po/hr.po b/po/hr.po index 5f87396be..b8ef064af 100644 --- a/po/hr.po +++ b/po/hr.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-05-17 15:49+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: Croatian " @@ -21578,49 +21654,49 @@ msgstr "" " • language-pack-gnome-xx (Za ručnu provjeru pogledaj poveznicu s uputama) " "za tvoj jezik " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " ili novija instalirana.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " ili novija)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz nije u STAZI sustava (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript nije u STAZI sustava (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Prošlo: verzija 0.5.x. je instalirana)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Zahtijeva verziju 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig nije pronađena, (zahtijeva verziju 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig instalirana, verzija nije dostupna" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " ili novija instalirana.) (modul enchant: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21629,7 +21705,7 @@ msgstr "" " • rcs %s još nije određeno (Prošlo: instalirana je verzija %s ili novija. " "Ako se ne koristi Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21638,20 +21714,20 @@ msgstr "" " • rcs %s još nije određeno (Zahtijeva instaliranu verziju %s ili noviju. " "Ako se ne koristi Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 biblioteka : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "pronađen je jedan drugi font" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21659,82 +21735,82 @@ msgstr "" "Za dodatak „Mrežni dijagram”, font White Rabbit daje izuzetno čitljiv rezultat.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "nije instaliran" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " i jedno od sljedećih: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") ili (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Instaliran(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Instaliran(Linux / Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "nije instaliran " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "nije pronađen." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" "Standardno. Prošlo: instalirani program – 32-bitni na 64-bitnom Win OS-u." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Zahtijeva dodatak za gramps, naveden u „Plugin lib”)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF instaliran" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".) (Zahtijeva verziju " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " instaliran.) (Prošlo: verzija " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(Otkrivanje lica pomoću OpenCV: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: nije pronađen. Zahtijeva verziju " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Zahtijeva: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Operacijski sustav: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Postavke jezika:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "nije postavljeno" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "nije testirano" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21780,7 +21856,7 @@ msgstr "" "ponovo instaliraj Gramps i obavezno odaberi sve prijevode i rječnike)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21788,13 +21864,13 @@ msgstr "" "\n" "Varijable Gramps okruženja:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "pronađeno" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26015,6 +26091,28 @@ msgstr "" " „%s”\n" "u tvom omiljenom web pregledniku …" +#~ msgid "Error accessing media object." +#~ msgstr "Greška pri pristupu medijskom objektu." + +#~ msgid "Server authorization error." +#~ msgstr "Greška autorizacije servera." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Greška autorizacije servera: nedovoljne dozvole." + +#~ msgid "Error: URL not found." +#~ msgstr "Greška: URL nije pronađen." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Greška %s tijekom povezivanja na server." + +#~ msgid "URL error while connecting to server." +#~ msgstr "URL greška tijekom povezivanja na server." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Nije moguće sinkronizirati promjene sa serverom." + #~ msgid "Search" #~ msgstr "Traži" diff --git a/po/hu.po b/po/hu.po index ded5d1526..a8c7809a3 100644 --- a/po/hu.po +++ b/po/hu.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: hu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-05 13:54+0000\n" "Last-Translator: Milan \n" "Language-Team: Hungarian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21658,19 +21714,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/is.po b/po/is.po index 35880b66c..ab2a13e2b 100644 --- a/po/is.po +++ b/po/is.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-04-30 20:09+0000\n" "Last-Translator: Sveinn í Felli \n" "Language-Team: Icelandic " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20788,19 +20852,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/it.po b/po/it.po index 06d61d5e0..0535b0f9a 100644 --- a/po/it.po +++ b/po/it.po @@ -67,7 +67,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-31 16:24+0000\n" "Last-Translator: medardo \n" "Language-Team: Italian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig not trovato, (richiesta versione 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig installato, versione non disponibile" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "trovato un altro tipo di carattere" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21465,87 +21537,87 @@ msgstr "" "href=\"https://www.fontsquirrel.com/fonts/white-rabbit\">White Rabbit " "fornisce un risultato molto leggibile.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 #, fuzzy #| msgid "Uninstall" msgid "not installed" msgstr "Disinstalla" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 #, fuzzy #| msgid "*Installed" msgid "Installed(Linux/Mac)" msgstr "*Installato" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "non installato " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 #, fuzzy #| msgid "*Installed" msgid "DBF installed" msgstr "*Installato" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21583,19 +21655,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25763,6 +25835,28 @@ msgstr "" " \"%s\"\n" " nel tuo navigatore web preferito ..." +#~ msgid "Error accessing media object." +#~ msgstr "Errore nell'accesso a oggetti multimediali." + +#~ msgid "Server authorization error." +#~ msgstr "Errore di autorizzazione del server." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Errore di autorizzazione del server: permessi insufficienti." + +#~ msgid "Error: URL not found." +#~ msgstr "Errore: URL non trovato." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Errore %s durante la connessione al server." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Errore URL durante la connessione al server." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Impossibile sincronizzare le modifiche al server." + #~ msgid "Search" #~ msgstr "Ricerca" diff --git a/po/ja.po b/po/ja.po index 1c0942fee..610a2361c 100644 --- a/po/ja.po +++ b/po/ja.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 3.3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-03-24 10:31+0000\n" "Last-Translator: coolz daddy \n" "Language-Team: Japanese " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21227,19 +21292,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/ka.po b/po/ka.po index ccd05e09b..75037add5 100644 --- a/po/ka.po +++ b/po/ka.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-09-13 09:49+0000\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20779,19 +20843,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/ln.po b/po/ln.po index 31e0e552c..24bc6358c 100644 --- a/po/ln.po +++ b/po/ln.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -15424,8 +15424,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15433,140 +15433,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15582,80 +15557,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16405,11 +16466,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20505,9 +20561,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20530,9 +20586,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20541,10 +20597,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20564,178 +20620,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20773,19 +20837,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/lt.po b/po/lt.po index cccda82c2..9994477d1 100644 --- a/po/lt.po +++ b/po/lt.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: lt\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-01-04 20:01+0000\n" "Last-Translator: openSUSE Lietuviškai \n" "Language-Team: Lithuanian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Reikalinga versija 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig nerasta (reikalinga versija 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "neįdiegta" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Įdiegta (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Įdiegta (Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "neįdiegta " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "nerasta." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF įdiegtas" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Reikalinga versija " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Operacinė sistema: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "neišbandyta" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21171,19 +21245,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "rasta" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/lv.po b/po/lv.po index b2a202744..207fc5b3c 100644 --- a/po/lv.po +++ b/po/lv.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -15425,8 +15425,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15434,140 +15434,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15583,80 +15558,174 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 #, python-format -msgid "Downloading %s media file(s)" +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:920 +#, python-format +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16406,11 +16475,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20507,9 +20571,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20532,9 +20596,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20543,10 +20607,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20566,178 +20630,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20775,19 +20847,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/mn.po b/po/mn.po index 05b746dc7..025c3cf1c 100644 --- a/po/mn.po +++ b/po/mn.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-04-07 13:46+0000\n" "Last-Translator: \"Batsaihan P.\" \n" "Language-Team: Mongolian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20797,19 +20861,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/nb.po b/po/nb.po index 75262b79a..35320646f 100644 --- a/po/nb.po +++ b/po/nb.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: nb\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-01 12:35+0000\n" "Last-Translator: Harald Herreros \n" "Language-Team: Norwegian Bokmål " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21088,19 +21152,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/ne.po b/po/ne.po index dac9bc69c..448b37689 100644 --- a/po/ne.po +++ b/po/ne.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -15424,8 +15424,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15433,140 +15433,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15582,80 +15557,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16405,11 +16466,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20505,9 +20561,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20530,9 +20586,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20541,10 +20597,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20564,178 +20620,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20773,19 +20837,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/nl.po b/po/nl.po index 27573913d..acf3c8a55 100644 --- a/po/nl.po +++ b/po/nl.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch " @@ -21741,49 +21815,49 @@ msgstr "" " • language-pack-gnome-xx (Handmatige controle zie instructies-link) voor " "jouw taal " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " of hoger geïnstalleerd.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " of hoger)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz niet in systeem PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript niet in systeem PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Geslaagd: versie 0.5.x is geïnstalleerd.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Vereist versie 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig niet aangetroffen, (Vereist versie 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig geïnstalleerd, versie niet beschikbaar" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " of hoger geïnstalleerd.) (enchant module: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21792,7 +21866,7 @@ msgstr "" " • rcs %s NTB (Passed: versie %s of hoger geïnstalleerd. Indien niet op " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21801,22 +21875,22 @@ msgstr "" " • rcs %s NTB (Vereist versie %s of hoger geïnstalleerd. Indien niet op " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" "exiv2 uitvoerbaar bestand niet geïnstalleerd, kan libexiv2-versie niet " "ophalen." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 bibliotheek : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "een ander lettertype gevonden" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21825,82 +21899,82 @@ msgstr "" "www.fontsquirrel.com/fonts/white-rabbit\">White Rabbit een buitengewoon " "leesbaar resultaat.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "niet geïnstalleerd" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " en een van beide: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") of (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Geïnstalleerd (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Geïnstalleerd (Linux / Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "niet geïnstalleerd " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "niet gevonden." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" "Standaard. Geslaagd: programma geïnstalleerd - 32-bits op 64-bits Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Vereist de Gramps-toevoeging vermeld onder 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF geïnstalleerd" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".) (Vereist versie " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " geïnstalleerd.) (Geslaagd: versie " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV gezichtsherkenning: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: niet gevonden. Vereist versie " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Vereist: MongoDB NTB / pymongo NTB" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Besturingssysteem: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Lokale " "Instellingen:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "niet ingesteld" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "niet getest" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21947,7 +22021,7 @@ msgstr "" "en woordenboeken selecteert)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21955,13 +22029,13 @@ msgstr "" "\n" "Gramps omgevingsvariabelen:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "gevonden" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26155,6 +26229,28 @@ msgstr "" " \"%s\" te openen\n" " in uw favoriete webnavigator..." +#~ msgid "Error accessing media object." +#~ msgstr "Fout bij het openen van het media-object." + +#~ msgid "Server authorization error." +#~ msgstr "Fout bij serverautorisatie." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Serverautorisatiefout: onvoldoende machtigingen." + +#~ msgid "Error: URL not found." +#~ msgstr "Fout: URL niet gevonden." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Fout %s tijdens het verbinden met de server." + +#~ msgid "URL error while connecting to server." +#~ msgstr "URL-fout tijdens het verbinden met de server." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Kan wijzigingen naar de server niet synchroniseren." + #~ msgid "Search" #~ msgstr "Zoeken" diff --git a/po/nn.po b/po/nn.po index c86a1a925..03eb08b5e 100644 --- a/po/nn.po +++ b/po/nn.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: nn\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2014-12-22 21:26+0100\n" "Last-Translator: \n" "Language-Team: Norwegian Nynorsk \n" @@ -15553,8 +15553,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15562,140 +15562,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15711,80 +15686,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16542,11 +16603,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20715,9 +20771,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20740,9 +20796,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20751,10 +20807,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20774,178 +20830,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20983,19 +21047,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/oc.po b/po/oc.po index 87bcc33a3..dd4c8f85e 100644 --- a/po/oc.po +++ b/po/oc.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -15424,8 +15424,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15433,140 +15433,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15582,80 +15557,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16405,11 +16466,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20505,9 +20561,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20530,9 +20586,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20541,10 +20597,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20564,178 +20620,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20773,19 +20837,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/pl.po b/po/pl.po index 56b35b2f3..5d1d42b0a 100644 --- a/po/pl.po +++ b/po/pl.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-12-14 21:00+0000\n" "Last-Translator: WaldiS \n" "Language-Team: Polish " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21828,21 +21899,21 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 #, fuzzy #| msgid "found: %s" msgid "found" msgstr "znaleziono: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26156,6 +26227,32 @@ msgid "" " into your preferred web navigator ..." msgstr "" +#~ msgid "Error accessing media object." +#~ msgstr "Błąd dostępu do obiektu multimedialnego." + +#~ msgid "Server authorization error." +#~ msgstr "Błąd autoryzacji serwera." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Błąd autoryzacji serwera: niewystarczające uprawnienia." + +#~ msgid "Error: URL not found." +#~ msgstr "Błąd: nie znaleziono adresu URL." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Błąd %s podczas łączenia z serwerem." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Błąd adresu URL podczas łączenia z serwerem." + +#, fuzzy +#~| msgid "" +#~| "Unable to synchronize changes to server: objects have been modified." +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "" +#~ "Nie można zsynchronizować zmian z serwerem: obiekty zostały zmodyfikowane." + #~ msgid "Search" #~ msgstr "Szukaj" diff --git a/po/pt_BR.po b/po/pt_BR.po index ea51b7aa9..1cd1ae1f2 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Andre Magri \n" "Language-Team: Portuguese (Brazil) " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21739,19 +21803,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/pt_PT.po b/po/pt_PT.po index faac5c6a3..ee2a67c94 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) " @@ -21566,49 +21640,49 @@ msgstr "" " • language-pack-gnome-xx (verificação manual, veja a ligação às instruções) " "para o seu idioma " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " ou superior instalada.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " ou superior)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "O Graphviz não está no PATH do sistema" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "O Ghostscript não está no PATH do sistema" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Aprovado: a versão 0.5.x está instalada.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Requer versão 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig em falta, requer versão 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig instalado, versão indisponível" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " ou superior instalado.) (módulo enchant: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21617,7 +21691,7 @@ msgstr "" " • rcs %s TBD (passou: versão %s ou superior instalada. Se não for Microsoft " "Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21626,21 +21700,21 @@ msgstr "" " • rcs %s TBD (requer a versão %s ou superior instalada. Se não for " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" "o executável exiv2 não está instalado, impossível obter a versão libexiv2." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (biblioteca Exiv2 : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "encontrou outra fonte" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21649,81 +21723,81 @@ msgstr "" "fonts/white-rabbit\">White Rabbit fornece um resultado extremamente " "legível.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "não instalado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " e um de: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") ou (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Instalado (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Instalado (Linux / Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "não instalado " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "não encontrado." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Padrão. Aprovado: programa instalado - 32 bits em Windows 64 bits." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Requer a extensão Gramps listada em \"Plugin lib\")" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF instalado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".) (Requer versão " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " instalado.) (Aprovado: versão " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(Detecção de rosto OpenCV: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: não encontrado. Requer versão " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Requer: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Sistema operativo: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Configurações " "locais:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "não configurado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "não testado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21770,7 +21844,7 @@ msgstr "" "dicionários)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21778,13 +21852,13 @@ msgstr "" "\n" "Variáveis de ambiente do Gramps:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "encontrado" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25970,6 +26044,28 @@ msgstr "" " \"%s\"\n" " no seu navegador web favorito..." +#~ msgid "Error accessing media object." +#~ msgstr "Erro ao aceder ao objecto multimédia." + +#~ msgid "Server authorization error." +#~ msgstr "Erro de autorização do servidor." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Erro de autorização do servidor: permissões insuficientes." + +#~ msgid "Error: URL not found." +#~ msgstr "Erro: URL não encontrado." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Erro %s durante a ligação ao servidor." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Erro de URL durante a ligação ao servidor." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Impossível sincronizar alterações com o servidor." + #~ msgid "Search" #~ msgstr "Procurar" diff --git a/po/ru.po b/po/ru.po index 2281652e8..a75706c8f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps50\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-09 08:01+0000\n" "Last-Translator: Vadim Barsukov \n" "Language-Team: Russian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " или выше.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " или выше)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz отсутствует в системном PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript отсутствует в системном PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Прошло: установлена версия 0.5.x.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Требуется версия 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 #, fuzzy #| msgid " (lxml: not found. Requires version " msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " (lxml: не найдено. Требуется версия " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " или выше.) (enchant module: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 library : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "нашел другой шрифт" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21915,82 +21987,82 @@ msgstr "" "fonts/white-rabbit\">White Rabbit обеспечивает чрезвычайно читаемый " "результат.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "не установлен" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") или (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Устанавливаемые (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Установленная (Linux / Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "не установлен " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "не найден." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Стандарт. Прошло: программа установлена - 32bit на 64bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" ") (Требуется аддон gramps, указанный в разделе \"Библиотека плагинов\")" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF установлен" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".) (Требуется версия " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " установлен.) (Прошло: версия " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(Распознавание лиц OpenCV: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: не найдено. Требуется версия " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Настройки локали:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "не задано" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "не проверено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -22037,7 +22109,7 @@ msgstr "" "словари)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -22045,13 +22117,13 @@ msgstr "" "\n" "Переменные среды Gramps:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "нашел" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26585,6 +26657,11 @@ msgstr "" " \"%s\"\n" " в Вашем браузере по умолчанию ..." +#, fuzzy +#~| msgid "not found." +#~ msgid "Error: URL not found." +#~ msgstr "не найден." + #~ msgid "Search" #~ msgstr "Поиск" diff --git a/po/sk.po b/po/sk.po index a5cce89d3..a5aa0e0b4 100644 --- a/po/sk.po +++ b/po/sk.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-08-01 16:13+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak " @@ -21560,49 +21642,49 @@ msgstr "" " • language-pack-gnome-xx (manuálna kontrola, pozri odkaz s pokynmi) pre váš " "jazyk " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " alebo vyššiu nainštalovanú verziu.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " alebo vyššiu)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz nie je v systémovom PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript nie je v systémovom PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Úspešné: verzia 0.5.x je nainštalovaná.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Vyžaduje verziu 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig nenájdený, (Vyžaduje verziu 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig nainštalovaný, verzia nedostupná" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " alebo vyššia verzia je nainštalovaná.) (enchant modul: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21611,7 +21693,7 @@ msgstr "" " • rcs %s TBD (Úspešné: nainštalovaná verzia %s alebo vyššia. Ak nie v " "systéme Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21620,22 +21702,22 @@ msgstr "" " • rcs %s TBD (vyžaduje nainštalovanú verziu %s alebo vyššiu. Ak nie v " "systéme Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" "Spustiteľný súbor exiv2 nie je nainštalovaný, nie je možné zistiť verziu " "knižnice libexiv2." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (knižnica Exiv2 : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "nájdený iný font" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21643,81 +21725,81 @@ msgstr "" "Pre doplnok Networkchart, písmo White Rabbit poskytuje mimoriadne čitateľný výsledok.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "nenainštalovaný" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " a jeden z buď:(pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") alebo (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Nainštalovaný (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Nainštalovaný (Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "nenainštalovaný " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "nenájdený." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Štandard. Úspšné: program nainštalovaný - 32bit na 64bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Vyžaduje doplnok gramps uvedený v časti 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF nainštalovaný" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Vyžaduje verziu " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " nainštalovaný.)(Úspešné: verzia " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV detekcia tváre: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml:nenájdený. Vyžaduje verziu " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Vyžaduje: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Operačný systém: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 #, fuzzy msgid "" "Locale Settings:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "nenastavené" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "netestované" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21765,7 +21847,7 @@ msgstr "" "slovníky)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21773,13 +21855,13 @@ msgstr "" "\n" "Premenné prostredia Gramps:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "nájdené" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25990,6 +26072,28 @@ msgstr "" " \"%s\" \n" " do svojho preferovaného webového prehliadača ..." +#~ msgid "Error accessing media object." +#~ msgstr "Chyba prístupu k mediálnemu objektu." + +#~ msgid "Server authorization error." +#~ msgstr "Chyba autorizácie servera." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Chyba autorizácie servera: nedostatočné oprávnenia." + +#~ msgid "Error: URL not found." +#~ msgstr "Chyba: Adresa URL sa nenašla." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Chyba %s pri pripájaní k serveru." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Chyba URL pri pripájaní k serveru." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Zmeny sa nedajú synchronizovať so serverom." + #~ msgid "Search" #~ msgstr "Vyhľadávanie" diff --git a/po/sl.po b/po/sl.po index 49f533a29..324583079 100644 --- a/po/sl.po +++ b/po/sl.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2008-02-22 23:19+0100\n" "Last-Translator: Bernard Banko \n" "Language-Team: lugos slovenizacija \n" @@ -15575,8 +15575,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15584,140 +15584,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 -#, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15733,80 +15708,182 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:920 +#, python-format +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Downloading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:1011 #, python-format -msgid "Uploading %s media file(s)" +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16569,11 +16646,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20721,9 +20793,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20746,9 +20818,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20757,10 +20829,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20780,178 +20852,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20989,19 +21069,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/sq.po b/po/sq.po index 7ac05c30f..179066d25 100644 --- a/po/sq.po +++ b/po/sq.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2008-11-13 21:00+0100\n" "Last-Translator: Vlora Jakupi \n" "Language-Team: \n" @@ -15541,8 +15541,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15550,140 +15550,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 -#, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15699,80 +15674,166 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 +#, python-format +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:920 #, python-format -msgid "Downloading %s media file(s)" +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:999 #, python-format -msgid "Uploading %s media file(s)" +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:1011 +#, python-format +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" +msgstr[1] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" +msgstr[1] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16534,11 +16595,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20684,9 +20740,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20709,9 +20765,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20720,10 +20776,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20743,178 +20799,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20952,19 +21016,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/sr.po b/po/sr.po index e2014e9b3..610ca8be7 100644 --- a/po/sr.po +++ b/po/sr.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-05-12 12:11+0000\n" "Last-Translator: Ранко Николић \n" "Language-Team: Serbian " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20797,19 +20869,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/sv.po b/po/sv.po index 9382cec28..3cc7a1d22 100644 --- a/po/sv.po +++ b/po/sv.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-05 22:49+0000\n" "Last-Translator: Pär Ekholm \n" "Language-Team: Swedish " @@ -21552,49 +21621,49 @@ msgstr "" " • language-pack-gnome-xx (Manuell kontroll se instruktionslänk) för ditt " "språk " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " eller högre installerad.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " eller högre)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz finns inte i system PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript finns inte i system PATH" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Framgång: version 0.5.x är installerad.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Kräver version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig inte hittad, (Kräver version 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig installerad, versionen är inte tillgänglig" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " eller högre installerad.) (enchant-modul: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21603,7 +21672,7 @@ msgstr "" " • rcs %s TBD (Godkänd: version %s eller senare installerad. Om inte på " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21612,21 +21681,21 @@ msgstr "" " • rcs %s TBD (Kräver version %s eller senare installerad. Om inte på " "Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" "exiv2-körbar fil är inte installerad, kan inte hämta libexiv2-versionen." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2-bibliotek : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "hittade ett annat typsnitt" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21634,81 +21703,81 @@ msgstr "" "För Networkchart-tillägget, typsnitt White Rabbit ger ett extremt läsbart resultat.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "inte installerad" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " och en av antingen: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") eller (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Installerad(MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Installerad(Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "inte installerad " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "inte hittad." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "Standard. Framgång: programmet installerat - 32bit på 64bit Win OS." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Kräver gramps-tillägg listat under 'Plugin lib')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF installerad" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Kräver version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " installerad.)(Framgång: version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV ansiktsigenkänning: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: inte hittad. Kräver version " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Kräver: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Operativsystem: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Språkinställningar:" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "inte inställd" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "inte testad" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21755,7 +21824,7 @@ msgstr "" "ordböcker)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21763,13 +21832,13 @@ msgstr "" "\n" "Gramps miljövariabler:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "hittad" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -25989,6 +26058,28 @@ msgstr "" "\"%s\"\n" "till din föredragna webbnavigator ..." +#~ msgid "Error accessing media object." +#~ msgstr "Fel vid åtkomst till medieobjektet." + +#~ msgid "Server authorization error." +#~ msgstr "Serverauktoriseringsfel." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Serverauktoriseringsfel: otillräckliga behörigheter." + +#~ msgid "Error: URL not found." +#~ msgstr "Fel: URL inte hittad." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Fel %s vid anslutning till servern." + +#~ msgid "URL error while connecting to server." +#~ msgstr "URL-fel vid anslutning till servern." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Det gick inte att synkronisera ändringarna med servern." + #~ msgid "Search" #~ msgstr "Sök" diff --git a/po/tr.po b/po/tr.po index 8fde60e48..429b9de06 100644 --- a/po/tr.po +++ b/po/tr.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish " @@ -21628,49 +21694,49 @@ msgstr "" " • language-pack-gnome-xx (Manuel kontrol, talimatlar bağlantısına bakın) " "Diliniz için " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " veya daha üstü yüklü.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " veya daha büyük)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz sistem YOLUNDA değil" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript sistem YOLUNDA değil" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Başarılı: 0.5.x sürümü yüklü.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (0.5.x sürümü gereklidir)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig bulunamadı, (0.5.x sürümü gereklidir)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig yüklü, sürüm bilgisi mevcut değil" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " veya daha üstü kurulu.) (büyü modülü: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21679,7 +21745,7 @@ msgstr "" " • rcs %s TBD (Geçti: %s veya daha üstü sürüm yüklü. Microsoft Windows'ta " "değilse)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21688,20 +21754,20 @@ msgstr "" " • rcs %s TBD (Microsoft Windows'da yüklü değilse, %s veya daha üst bir " "sürümün kurulu olması gerekir)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "exiv2 yürütülebilir dosyası yüklü değil, libexiv2 sürümü alınamıyor." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Exiv2 kütüphanesi : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "başka bir yazı tipi bulundu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21710,83 +21776,83 @@ msgstr "" "white-rabbit\">White Rabbit yazı tipi son derece okunaklı bir sonuç " "sağlar.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "yüklenmedi" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " ve bunlardan biri: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") veya (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Yüklü (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Yüklü (Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "yüklenmedi " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "bulunamadı." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" "Standart. Geçti: program yüklü - 64 bit Windows işletim sisteminde 32 bit." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" ") ('Eklenti kütüphanesi' altında listelenen gramps eklentisini gerektirir)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF yüklü" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Sürüm gerektirir " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " yüklü.)(Geçti: sürüm " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV yüz algılama: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: bulunamadı. Sürüm gerektirir " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Gerektirir: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • İşletim Sistemi: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Yerel Ayarlar:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "ayarlanmadı" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "test edilmedi" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21833,7 +21899,7 @@ msgstr "" "olun)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -21841,13 +21907,13 @@ msgstr "" "\n" "Gramps Ortam değişkenleri:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "bulundu" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26078,6 +26144,28 @@ msgstr "" " \"%s\"\n" " web gezgininde açmayı deneyin..." +#~ msgid "Error accessing media object." +#~ msgstr "Medya nesnesine erişimde hata oluştu." + +#~ msgid "Server authorization error." +#~ msgstr "Sunucu yetkilendirme hatası." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Sunucu yetkilendirme hatası: Yetersiz izinler." + +#~ msgid "Error: URL not found." +#~ msgstr "Hata: URL bulunamadı." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Sunucuya bağlanırken %s hatası oluştu." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Sunucuya bağlanırken URL hatası oluştu." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Değişiklikler sunucuya senkronize edilemedi." + #~ msgid "Search" #~ msgstr "Arama" diff --git a/po/uk.po b/po/uk.po index 566e8db94..522507ac0 100644 --- a/po/uk.po +++ b/po/uk.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2025-11-17 06:51+0000\n" "Last-Translator: Fedir Zinchuk \n" "Language-Team: Ukrainian " @@ -21802,49 +21878,49 @@ msgstr "" " • language-pack-gnome-xx (Ручна перевірка, див. посилання з інструкцією) " "для вашої мови <показати локаль тут (TBD)>" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr " або новіша встановлена.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr " або новіша)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "Graphviz не в системному шляху (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "Ghostscript не в системному шляху (PATH)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr " (Успішно: встановлено версію 0.5.x.)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr " (Потрібна версія 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr " • python-fontconfig не знайдено, (потрібна версія 0.5.x)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr " • python-fontconfig встановлено, версія недоступна" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr " або новіша встановлена.) (модуль enchant: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " @@ -21853,7 +21929,7 @@ msgstr "" " • rcs %s TBD (Успішно: встановлена версія %s або новіша. Якщо не Microsoft " "Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " @@ -21861,20 +21937,20 @@ msgid "" msgstr "" " • rcs %s TBD (Потрібна версія %s або новіша. Якщо не Microsoft Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "GExiv2 : %s (Бібліотека Exiv2 : %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "знайдено інший шрифт" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" @@ -21883,83 +21959,83 @@ msgstr "" "white-rabbit\">White Rabbit забезпечує надзвичайно читабельний " "результат.\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "не встановлено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr " і один з наступних: (pydotplus: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr ") або (pygraphviz: " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "Встановлено (MS-Windows)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "Встановлено (Linux/Mac)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "не встановлено " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "не знайдено." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" "Стандарт. Успішно: програму встановлено - 32-бітна версія на 64-бітній ОС " "Windows." -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr ") (Потрібен додаток gramps, вказаний у 'Бібліотеці плагінів')" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "DBF встановлено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr ".)(Потрібна версія " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr " встановлено.)(Успішно: версія " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "(OpenCV розпізнавання облич: %s)" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr " (lxml: не знайдено. Потрібна версія " -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr " • Потрібно: MongoDB TBD / pymongo TBD" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr " • Операційна система: %s" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "Налаштування " "Локалей:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "не встановлено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "не перевірено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -22005,7 +22081,7 @@ msgstr "" "перевстановіть Gramps і переконайтеся, що обрали всі Переклади та Словники)\n" "\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" @@ -22013,13 +22089,13 @@ msgstr "" "\n" "Змінні середовища Gramps:\n" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "знайдено" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" @@ -26254,6 +26330,28 @@ msgstr "" " \"%s\"\n" " у вашому улюбленому веб-навігаторі..." +#~ msgid "Error accessing media object." +#~ msgstr "Помилка доступу до медіаобʼєкта." + +#~ msgid "Server authorization error." +#~ msgstr "Помилка авторизації на сервері." + +#~ msgid "Server authorization error: insufficient permissions." +#~ msgstr "Помилка авторизації на сервері: недостатньо прав." + +#~ msgid "Error: URL not found." +#~ msgstr "Помилка: URL не знайдено." + +#, python-format +#~ msgid "Error %s while connecting to server." +#~ msgstr "Помилка %s під час підключення до сервера." + +#~ msgid "URL error while connecting to server." +#~ msgstr "Помилка URL під час підключення до сервера." + +#~ msgid "Unable to synchronize changes to server." +#~ msgstr "Не вдалося синхронізувати зміни із сервером." + #~ msgid "Search" #~ msgstr "Пошук" diff --git a/po/vi.po b/po/vi.po index 84ba367cf..54d57f39e 100644 --- a/po/vi.po +++ b/po/vi.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VIETNAMESE 4.2.8\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-02-08 09:09+0000\n" "Last-Translator: Securitocat \n" "Language-Team: Vietnamese " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20934,19 +20990,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/zh_CN.po b/po/zh_CN.po index d7746438a..65f5b0f86 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VERSION 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Tian Shixiong \n" "Language-Team: Chinese (Simplified Han script) " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -21002,19 +21058,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/zh_HK.po b/po/zh_HK.po index d4b5e54a9..46f30b3a7 100644 --- a/po/zh_HK.po +++ b/po/zh_HK.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (Hong Kong) <(nothing)>\n" @@ -15437,8 +15437,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15446,140 +15446,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 -#, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15595,80 +15570,158 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 #, python-format -msgid "Downloading %s media file(s)" +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:920 +#, python-format +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:999 +#, python-format +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:1011 #, python-format -msgid "Uploading %s media file(s)" +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16418,11 +16471,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20524,9 +20572,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20549,9 +20597,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20560,10 +20608,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20583,178 +20631,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20792,19 +20848,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" diff --git a/po/zh_TW.po b/po/zh_TW.po index bee8f8726..0ede4fcde 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-29 09:58-0700\n" +"POT-Creation-Date: 2026-08-01 09:38-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (traditional) \n" @@ -15437,8 +15437,8 @@ msgstr "" msgid "AI Chatbot Gramplet (requires connecting to an LLM service)" msgstr "" -#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:118 -#: GrampsWebSync/grampswebsync.py:212 +#: GrampsWebSync/grampswebsync.gpr.py:29 GrampsWebSync/grampswebsync.py:213 +#: GrampsWebSync/grampswebsync.py:271 msgid "Gramps Web Sync" msgstr "" @@ -15446,140 +15446,115 @@ msgstr "" msgid "Synchronizes a local database with a Gramps Web instance." msgstr "" -#: GrampsWebSync/grampswebsync.py:276 -msgid "Your user does not have sufficient server permissions to use sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:282 -msgid "Fetching remote data..." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:298 -msgid "Unexpected error while applying changes." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:336 -msgid "Media files are in sync." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:345 +#: GrampsWebSync/grampswebsync.py:124 #, python-format -msgid "Successfully downloaded %s media files." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:348 -#, python-format -msgid "Encountered %s errors during download." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:354 -#, python-format -msgid "Successfully uploaded %s media files." +msgid "" +"The system keyring could not be used. Snap confinement blocks access until " +"you run: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:357 -#, python-format -msgid "Encountered %s errors during upload." +#: GrampsWebSync/grampswebsync.py:128 +msgid "" +"The system keyring could not be used. You will need to enter your password " +"each time." msgstr "" -#: GrampsWebSync/grampswebsync.py:375 +#: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:379 +#: GrampsWebSync/grampswebsync.py:148 msgid "Access forbidden. Please check username and password." msgstr "" -#: GrampsWebSync/grampswebsync.py:383 +#: GrampsWebSync/grampswebsync.py:150 msgid "GrampsWeb service not found. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:387 +#: GrampsWebSync/grampswebsync.py:152 msgid "Too many requests, please try again in a few seconds." msgstr "" -#: GrampsWebSync/grampswebsync.py:390 +#: GrampsWebSync/grampswebsync.py:154 msgid "GrampsWeb tree is disabled." msgstr "" -#: GrampsWebSync/grampswebsync.py:393 -#, python-format -msgid "Server error %s. Please check your connection." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:399 +#: GrampsWebSync/grampswebsync.py:156 msgid "Connection failed. Please check the URL and your internet connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:405 +#: GrampsWebSync/grampswebsync.py:159 msgid "Invalid server response. Please check the URL." msgstr "" -#: GrampsWebSync/grampswebsync.py:409 -#, python-format -msgid "Unexpected error: %s" -msgstr "" - -#: GrampsWebSync/grampswebsync.py:453 GrampsWebSync/grampswebsync.py:478 -msgid "Error accessing media object." +#: GrampsWebSync/grampswebsync.py:162 +msgid "Your user does not have sufficient server permissions to use sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:522 +#: GrampsWebSync/grampswebsync.py:164 msgid "Failed importing downloaded XML file." msgstr "" -#: GrampsWebSync/grampswebsync.py:527 -msgid "Comparing local and remote data..." +#: GrampsWebSync/grampswebsync.py:166 +msgid "Unable to synchronize changes to server: objects have been modified." msgstr "" -#: GrampsWebSync/grampswebsync.py:576 -msgid "Server authorization error." +#: GrampsWebSync/grampswebsync.py:168 +msgid "Unexpected error while applying changes." msgstr "" -#: GrampsWebSync/grampswebsync.py:579 -msgid "Server authorization error: insufficient permissions." +#: GrampsWebSync/grampswebsync.py:170 +msgid "" +"The family tree was modified while the changes were being reviewed. Nothing " +"has been applied. Please compare again." msgstr "" -#: GrampsWebSync/grampswebsync.py:582 -msgid "Error: URL not found." +#: GrampsWebSync/grampswebsync.py:175 +#, python-format +msgid "The server could not apply the changes: %s" msgstr "" -#: GrampsWebSync/grampswebsync.py:586 -msgid "Unable to synchronize changes to server: objects have been modified." +#: GrampsWebSync/grampswebsync.py:177 +#, python-format +msgid "Server error %s. Please check your connection." msgstr "" -#: GrampsWebSync/grampswebsync.py:590 +#: GrampsWebSync/grampswebsync.py:179 GrampsWebSync/grampswebsync.py:180 #, python-format -msgid "Error %s while connecting to server." +msgid "Unexpected error: %s" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:341 +msgid "Fetching remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:593 -msgid "URL error while connecting to server." +#: GrampsWebSync/grampswebsync.py:344 +msgid "Comparing local and remote data..." msgstr "" -#: GrampsWebSync/grampswebsync.py:597 -msgid "Unable to synchronize changes to server." +#: GrampsWebSync/grampswebsync.py:348 +msgid "Successfully applied changes to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:626 +#: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" msgstr "" -#: GrampsWebSync/grampswebsync.py:628 +#: GrampsWebSync/grampswebsync.py:439 msgid "" "You have specified a URL with http scheme. If you continue, your password " "will be sent in clear text over the network. Use only for local testing!" msgstr "" -#: GrampsWebSync/grampswebsync.py:633 +#: GrampsWebSync/grampswebsync.py:444 msgid "Continue with HTTP" msgstr "" -#: GrampsWebSync/grampswebsync.py:634 +#: GrampsWebSync/grampswebsync.py:445 msgid "Use HTTPS" msgstr "" -#: GrampsWebSync/grampswebsync.py:762 +#: GrampsWebSync/grampswebsync.py:495 msgid "" "This tool allows to synchronize the currently opened family tree with a " "remote family tree served by Gramps Web.\n" @@ -15595,80 +15570,158 @@ msgid "" "option to make manual modifications, use the Import Merge Tool instead." msgstr "" -#: GrampsWebSync/grampswebsync.py:789 +#: GrampsWebSync/grampswebsync.py:522 msgid "Server URL: " msgstr "" -#: GrampsWebSync/grampswebsync.py:898 +#: GrampsWebSync/grampswebsync.py:578 HistContext/HistContext.py:289 +#: HistContext/HistContext.py:333 HistContext/HistContext.py:420 +msgid "Error:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:646 +msgid "Sync mode" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:656 +msgid "" +"Changes from both sides are combined. Objects edited in both places are " +"merged." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:660 +msgid "" +"The server is made to match this computer. Anything changed only on the " +"server is discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:664 +msgid "" +"This computer is made to match the server. Anything changed only here is " +"discarded." +msgstr "" + +#: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" msgstr "" -#: GrampsWebSync/grampswebsync.py:907 +#: GrampsWebSync/grampswebsync.py:672 msgid "Reset remote to local" msgstr "" -#: GrampsWebSync/grampswebsync.py:915 +#: GrampsWebSync/grampswebsync.py:673 msgid "Reset local to remote" msgstr "" -#: GrampsWebSync/grampswebsync.py:948 +#: GrampsWebSync/grampswebsync.py:712 +msgid "Warning:" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:953 +#: GrampsWebSync/grampswebsync.py:727 msgid "Remote changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:958 +#: GrampsWebSync/grampswebsync.py:732 msgid "Simultaneous changes" msgstr "" -#: GrampsWebSync/grampswebsync.py:1015 +#: GrampsWebSync/grampswebsync.py:790 msgid "Fetching information about media files..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1035 +#: GrampsWebSync/grampswebsync.py:816 msgid "Both trees are the same." msgstr "" -#: GrampsWebSync/grampswebsync.py:1041 +#: GrampsWebSync/grampswebsync.py:822 msgid "Applying changes to local database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1043 +#: GrampsWebSync/grampswebsync.py:824 msgid "No changes to apply to local database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1047 +#: GrampsWebSync/grampswebsync.py:828 msgid "Applying changes to remote database ..." msgstr "" -#: GrampsWebSync/grampswebsync.py:1052 +#: GrampsWebSync/grampswebsync.py:833 msgid "No changes to apply to remote database." msgstr "" -#: GrampsWebSync/grampswebsync.py:1060 -msgid "Successfully applied changes to local database." -msgstr "" - -#: GrampsWebSync/grampswebsync.py:1089 +#: GrampsWebSync/grampswebsync.py:862 msgid "Missing locally" msgstr "" -#: GrampsWebSync/grampswebsync.py:1092 +#: GrampsWebSync/grampswebsync.py:865 msgid "Missing remotely" msgstr "" -#: GrampsWebSync/grampswebsync.py:1130 +#: GrampsWebSync/grampswebsync.py:905 #, python-format -msgid "Downloading %s media file(s)" +msgid "Downloading %s media file" +msgid_plural "Downloading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:920 +#, python-format +msgid "Uploading %s media file" +msgid_plural "Uploading %s media files" +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:965 +msgid "Try again" +msgstr "" + +#: GrampsWebSync/grampswebsync.py:999 +#, python-format +msgid "Applied %s change." +msgid_plural "Applied %s changes." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1006 +msgid "Media files are in sync." msgstr "" -#: GrampsWebSync/grampswebsync.py:1138 +#: GrampsWebSync/grampswebsync.py:1011 #, python-format -msgid "Uploading %s media file(s)" +msgid "%s media file is missing on both sides and could not be transferred." +msgid_plural "" +"%s media files are missing on both sides and could not be transferred." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1020 +msgid "Both trees are already in sync." msgstr "" +#: GrampsWebSync/grampswebsync.py:1033 +#, python-format +msgid "Successfully downloaded %s media file." +msgid_plural "Successfully downloaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1043 +#, python-format +msgid "Encountered %s error during download." +msgid_plural "Encountered %s errors during download." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1056 +#, python-format +msgid "Successfully uploaded %s media file." +msgid_plural "Successfully uploaded %s media files." +msgstr[0] "" + +#: GrampsWebSync/grampswebsync.py:1065 +#, python-format +msgid "Encountered %s error during upload." +msgid_plural "Encountered %s errors during upload." +msgstr[0] "" + #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" msgstr "" @@ -16418,11 +16471,6 @@ msgstr "" msgid " in line: " msgstr "" -#: HistContext/HistContext.py:289 HistContext/HistContext.py:333 -#: HistContext/HistContext.py:420 -msgid "Error:" -msgstr "" - #: HistContext/HistContext.py:327 msgid ": line does not contain four sections separated by semicolons in : \"" msgstr "" @@ -20524,9 +20572,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:608 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:661 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:729 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:913 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1354 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:923 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1143 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1364 msgid " (Requires version " msgstr "" @@ -20549,9 +20597,9 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:670 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:691 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:738 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:905 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1123 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1346 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1133 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 msgid " (Passed: version " msgstr "" @@ -20560,10 +20608,10 @@ msgstr "" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:648 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:649 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:716 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:894 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:966 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:967 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1295 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:904 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:976 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:977 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1305 msgid "unknown version" msgstr "" @@ -20583,178 +20631,186 @@ msgstr "" msgid " (Requires " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:789 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:806 msgid " • Berkeley Database library (bsddb3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:827 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:791 +msgid "" +")\n" +"\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " +"database" +msgstr "" + +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:844 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:854 msgid " • xdg-utils (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:857 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:867 msgid " • librsvg2 (Manual check see instructions link)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:869 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:879 msgid "" " • language-pack-gnome-xx (Manual check see instructions link) for your " "Language " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:907 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1348 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1356 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1798 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1807 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2021 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:917 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1358 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1366 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1808 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1817 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2031 msgid " or greater installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:915 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:925 msgid " or greater)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:948 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:958 msgid "Graphviz not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1014 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1024 msgid "Ghostscript not in system PATH" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1032 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1042 msgid " (Passed: version 0.5.x is installed.)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1035 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1045 msgid " (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1037 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1047 msgid " • python-fontconfig not found, (Requires version 0.5.x)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1039 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1049 msgid " • python-fontconfig installed, version unavailable" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1125 #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1135 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1145 msgid " or greater installed.) (enchant module: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1194 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1204 #, python-format msgid "" " • rcs %s TBD (Passed: version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1199 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1209 #, python-format msgid "" " • rcs %s TBD (Requires version %s or greater installed. If not on Microsoft " "Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1316 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1326 msgid "exiv2 executable not installed, can't retrieve libexiv2 version." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1317 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1327 #, python-format msgid "GExiv2 : %s (Exiv2 library : %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1394 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1404 msgid "found another font" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1403 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1413 msgid "" "For addon Networkchart, font White Rabbit provides an extremely readable result.\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1447 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1505 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1516 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1527 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1457 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1515 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1526 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 msgid "not installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1535 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1545 msgid " and one of either: (pydotplus: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1537 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1547 msgid ") or (pygraphviz: " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1566 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1576 msgid "Installed(MS-Windows)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1570 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1580 msgid "Installed(Linux/Mac)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1607 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1617 msgid "not installed " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1664 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1860 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1862 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1863 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1952 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1959 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1674 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1870 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1872 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1873 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1969 msgid "not found." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1690 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1700 msgid "Standard. Passed: program installed - 32bit on 64bit Win OS." msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1749 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1759 msgid ") (Requires the gramps addon listed under 'Plugin lib')" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1785 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1795 msgid "DBF installed" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1796 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1806 msgid ".)(Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1805 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1815 msgid " installed.)(Passed: version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1962 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:1972 #, python-format msgid "(OpenCV facedetection: %s)" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2019 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2029 msgid " (lxml: not found. Requires version " msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2041 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2051 msgid " • Requires: MongoDB TBD / pymongo TBD" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2055 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2065 #, python-format msgid " • Operating System: %s" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2078 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2086 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2087 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2088 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2089 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2150 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2151 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2152 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2153 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2096 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2097 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2098 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2099 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2160 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2162 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2163 msgid "not set" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2091 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2092 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2093 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2094 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2155 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2156 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2157 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2158 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2101 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2102 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2103 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2104 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2165 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2166 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2167 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2168 msgid "not tested" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2123 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2133 msgid "" "\n" "Installed Locales\\Translations (If only English is listed please re-install " @@ -20792,19 +20848,19 @@ msgid "" "\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2161 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2171 msgid "" "\n" "Gramps Environment variables:\n" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2200 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2263 -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2300 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2210 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2273 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2310 msgid "found" msgstr "" -#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2309 +#: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:2319 msgid "" "Sphinx is a tool that builds the Gramps development documentation and man " "pages\n" From e4fe1bb305d6e6b75b960b94175b4f5cb3303717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirko=20Leonh=C3=A4user?= Date: Wed, 5 Aug 2026 20:02:19 +0200 Subject: [PATCH 115/156] Translated using Weblate (German) Currently translated at 100.0% (5586 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/de/ --- po/de.po | 75 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/po/de.po b/po/de.po index 9ec1fb9fc..56af13a84 100644 --- a/po/de.po +++ b/po/de.po @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-07-30 22:02+0000\n" +"PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German \n" @@ -16221,12 +16221,16 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"Der System-Schlüsselbund konnte nicht verwendet werden. Die Snap-" +"Einschränkung blockiert den Zugriff, bis du folgenden Befehl ausführst: %s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." msgstr "" +"Der System-Schlüsselbund konnte nicht verwendet werden. Du musst dein " +"Passwort jedes Mal eingeben." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -16286,11 +16290,13 @@ msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." msgstr "" +"Der Stammbaum wurde während der Überprüfung der Änderungen modifiziert. Es " +"wurden noch keine Änderungen übernommen. Bitte vergleiche die Daten erneut." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "Der Server konnte die Änderungen nicht übernehmen: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -16378,25 +16384,31 @@ msgstr "Fehler:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "Synchronisierungsmodus" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." msgstr "" +"Änderungen von beiden Seiten werden zusammengeführt. Objekte, die an beiden " +"Orten bearbeitet wurden, werden zusammengeführt." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." msgstr "" +"Der Server ist auf diesen Computer abgestimmt. Alle Änderungen, die " +"ausschließlich auf dem Server vorgenommen wurden, werden verworfen." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." msgstr "" +"Dieser Computer ist auf den Server abgestimmt. Alle Änderungen, die " +"ausschließlich hier vorgenommen wurden, werden verworfen." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -16411,10 +16423,8 @@ msgid "Reset local to remote" msgstr "Lokal auf entfernt zurücksetzen" #: GrampsWebSync/grampswebsync.py:712 -#, fuzzy -#| msgid "Learning" msgid "Warning:" -msgstr "Lernen" +msgstr "Warnung:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16461,31 +16471,29 @@ msgid "Missing remotely" msgstr "Fehlt entfernt" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file(s)" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" -msgstr[0] "%s Mediendatei(en) werden heruntergeladen" -msgstr[1] "%s Mediendatei(en) werden heruntergeladen" +msgstr[0] "%s Mediendatei wird heruntergeladen" +msgstr[1] "%s Mediendateien werden heruntergeladen" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file(s)" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" -msgstr[0] "%s Mediendatei(en) werden hochgeladen" -msgstr[1] "%s Mediendatei(en) werden hochgeladen" +msgstr[0] "%s Mediendatei wird hochgeladen" +msgstr[1] "%s Mediendateien werden hochgeladen" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "Versuche es noch einmal" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%s Änderung wurde übernommen." +msgstr[1] "%s Änderungen wurden übernommen." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16497,44 +16505,42 @@ msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." msgstr[0] "" +"%s Mediendatei fehlt auf beiden Seiten und konnte daher nicht übertragen " +"werden." msgstr[1] "" +"%s Mediendateien fehlen auf beiden Seiten und konnten nicht übertragen " +"werden." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "Beide Bäume sind gleich." +msgstr "Beide Bäume sind bereits synchronisiert." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "%s Mediendateien wurden erfolgreich heruntergeladen." +msgstr[0] "%s Mediendatei wurde erfolgreich heruntergeladen." msgstr[1] "%s Mediendateien wurden erfolgreich heruntergeladen." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." -msgstr[0] "Beim Herunterladen sind %s Fehler aufgetreten." +msgstr[0] "Beim Herunterladen ist %s Fehler aufgetreten." msgstr[1] "Beim Herunterladen sind %s Fehler aufgetreten." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "%s Mediendatei wurden erfolgreich hochgeladen." -msgstr[1] "%s Mediendatei wurden erfolgreich hochgeladen." +msgstr[0] "%s Mediendatei wurde erfolgreich hochgeladen." +msgstr[1] "%s Mediendateien wurden erfolgreich hochgeladen." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "Beim Hochladen sind %s Fehler aufgetreten." +msgstr[0] "Beim Hochladen ist %s Fehler aufgetreten." msgstr[1] "Beim Hochladen sind %s Fehler aufgetreten." #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 @@ -21909,6 +21915,9 @@ msgid "" "\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " "database" msgstr "" +")\n" +"\tErfordert BerkeleyDB oder den Python-Adapter „Python-bsddb3“ sowie die " +"Berkeley-Datenbank" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " From d320b3773c3facdb8aa1e9205e9391a77429a166 Mon Sep 17 00:00:00 2001 From: Avi Markovitz Date: Wed, 5 Aug 2026 20:02:19 +0200 Subject: [PATCH 116/156] Translated using Weblate (Hebrew) Currently translated at 100.0% (5586 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ Translated using Weblate (Hebrew) Currently translated at 99.6% (5567 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/he/ --- po/he.po | 102 ++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/po/he.po b/po/he.po index 5fbcb4218..f34cc38cf 100644 --- a/po/he.po +++ b/po/he.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-08-01 16:13+0000\n" +"PO-Revision-Date: 2026-08-03 20:31+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew \n" @@ -19,7 +19,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " "n % 10 == 0) ? 2 : 3));\n" -"X-Generator: Weblate 2026.8.dev0\n" +"X-Generator: Weblate 2026.8.1.dev0\n" msgid "Birthdays" msgstr "ימי הולדת" @@ -15805,12 +15805,14 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"לא ניתן היה להשתמש בצרור־מפתחות המערכת. הגבלת Snap חוסמת את הגישה עד להרצת: " +"%s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." -msgstr "" +msgstr "לא ניתן היה להשתמש בצרור מפתחות המערכת. תדרש הזנת סיסמה בכל פעם." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -15860,12 +15862,12 @@ msgstr "שגיאה בלתי צפויה בעת החלת שינויים." msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." -msgstr "" +msgstr "אילן־היוחסין הוסגל במהלך סקירת השינויים. לא הוחל דבר. נא להשוות שוב." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "השרת לא הצליח להחיל שינויים: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -15947,25 +15949,25 @@ msgstr "שגיאה:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "מצב סינכרון" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." -msgstr "" +msgstr "שינויים משני הצדדים שולבו. עצמים שנערכו בשני המקומות מוזגו." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." -msgstr "" +msgstr "השרת בנוי כך שהתאמות יתבצעו למחשב זה. כל שינוי שנעשה רק בשרת יושלך." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." -msgstr "" +msgstr "מחשב זה בנוי כך שהתאמות יתבצעו לשרת. כל שינוי שנעשה רק כאן יושלך." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -15980,10 +15982,8 @@ msgid "Reset local to remote" msgstr "שיצוב מצב מקומי למרוחק" #: GrampsWebSync/grampswebsync.py:712 -#, fuzzy -#| msgid "Learning" msgid "Warning:" -msgstr "למידה" +msgstr "אזהרה:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16030,37 +16030,35 @@ msgid "Missing remotely" msgstr "חסרים באופן מרוחק" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file(s)" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" -msgstr[0] "הורדת %s מדיה קובץ(s)" -msgstr[1] "הורדת %s מדיה קובץ(s)" -msgstr[2] "הורדת %s מדיה קובץ(s)" -msgstr[3] "הורדת %s מדיה קובץ(s)" +msgstr[0] "הורדת קובץ מדיה %s" +msgstr[1] "הורדת %s קבצי מדיה" +msgstr[2] "הורדת %s קבצי מדיה" +msgstr[3] "הורדת %s קבצי מדיה" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file(s)" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" -msgstr[0] "העלאת %s קובצי מדיה" -msgstr[1] "העלאת %s קובצי מדיה" -msgstr[2] "העלאת %s קובצי מדיה" -msgstr[3] "העלאת %s קובצי מדיה" +msgstr[0] "העלאת קובץ מדיה %s" +msgstr[1] "העלאת %s קבצי מדיה" +msgstr[2] "העלאת %s קבצי מדיה" +msgstr[3] "העלאת %s קבצי מדיה" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "נא לנסות שוב" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgstr[0] "הוחל שינוי %s." +msgstr[1] "הוחלו %s שינויים." +msgstr[2] "הוחלו %s שינויים." +msgstr[3] "הוחלו %s שינויים." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16071,53 +16069,47 @@ msgstr "קובצי המדיה מסונכרנים." msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgstr[0] "קובץ מדיה %s חסר בשני הצדדים ולא ניתנים להעברה." +msgstr[1] "%s קבצי מדיה חסרים בשני הצדדים ולא ניתנו להעברה." +msgstr[2] "%s קבצי מדיה חסרים בשני הצדדים ולא ניתנו להעברה." +msgstr[3] "%s קבצי מדיה חסרים בשני הצדדים ולא ניתנו להעברה." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "שני אילנות הם אותו." +msgstr "שני אילנות־היוחסין מסונכרים כבר." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "הורדת %s קובצי מדיה הושלמה בהצלחה." -msgstr[1] "הורדת %s קובצי מדיה הושלמה בהצלחה." -msgstr[2] "הורדת %s קובצי מדיה הושלמה בהצלחה." -msgstr[3] "הורדת %s קובצי מדיה הושלמה בהצלחה." +msgstr[0] "הורדת קובץ מדיה %s צלחה." +msgstr[1] "הורדת %s קבצי מדיה צלחה." +msgstr[2] "הורדת %s קבצי מדיה צלחה." +msgstr[3] "הורדת %s קבצי מדיה צלחה." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." -msgstr[0] "אירעו %s שגיאות במהלך ההורדה." +msgstr[0] "אירעה שגיאה %s במהלך ההורדה." msgstr[1] "אירעו %s שגיאות במהלך ההורדה." msgstr[2] "אירעו %s שגיאות במהלך ההורדה." msgstr[3] "אירעו %s שגיאות במהלך ההורדה." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "העלאת %s קובצי מדיה הושלמה בהצלחה." -msgstr[1] "העלאת %s קובצי מדיה הושלמה בהצלחה." -msgstr[2] "העלאת %s קובצי מדיה הושלמה בהצלחה." -msgstr[3] "העלאת %s קובצי מדיה הושלמה בהצלחה." +msgstr[0] "העלאת קובץ מדיה %s צלחה." +msgstr[1] "העלאת %s קבצי מדיה צלחה." +msgstr[2] "העלאת %s קבצי מדיה צלחה." +msgstr[3] "העלאת %s קבצי מדיה צלחה." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "אירעו %s שגיאות במהלך ההעלאה." +msgstr[0] "אירעה שגיאה %s במהלך ההעלאה." msgstr[1] "אירעו %s שגיאות במהלך ההעלאה." msgstr[2] "אירעו %s שגיאות במהלך ההעלאה." msgstr[3] "אירעו %s שגיאות במהלך ההעלאה." @@ -21292,6 +21284,8 @@ msgid "" "\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " "database" msgstr "" +")\n" +"\tנדרש מתאם BerkeleyDB או Python-bsddb3 Python ומסד־נתוני Berkeley" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " From 35a8ad2513b6fefda56b74a66138562395c1b284 Mon Sep 17 00:00:00 2001 From: Stephan Paternotte Date: Wed, 5 Aug 2026 20:02:19 +0200 Subject: [PATCH 117/156] Translated using Weblate (Dutch) Currently translated at 100.0% (5586 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/nl/ --- po/nl.po | 81 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/po/nl.po b/po/nl.po index acf3c8a55..c924e3e7f 100644 --- a/po/nl.po +++ b/po/nl.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-08-01 16:13+0000\n" +"PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch \n" @@ -16144,12 +16144,16 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"De systeemsleutelring kon niet worden gebruikt. Snap-beperking blokkeert de " +"toegang totdat u het volgende uitvoert: %s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." msgstr "" +"De systeemsleutelring kon niet worden gebruikt. U moet elke keer uw " +"wachtwoord invoeren." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -16203,11 +16207,13 @@ msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." msgstr "" +"De stamboom werd aangepast tijdens het controleren van de wijzigingen. Er is " +"niets toegepast. Vergelijk nog eens." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "De server kon de wijzigingen niet toepassen: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -16229,7 +16235,7 @@ msgstr "Lokale en externe gegevens vergelijken…" #: GrampsWebSync/grampswebsync.py:348 msgid "Successfully applied changes to local database." -msgstr "Wijzigingen zijn toegepast op lokale database." +msgstr "Wijzigingen zijn met succes toegepast op lokale database." #: GrampsWebSync/grampswebsync.py:437 msgid "Continue without transport encryption?" @@ -16294,25 +16300,31 @@ msgstr "Fout:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "Synchronisatiemodus" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." msgstr "" +"Veranderingen van beide kanten worden gecombineerd. Objecten die op beide " +"plaatsen zijn bewerkt, worden samengevoegd." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." msgstr "" +"De server is ingesteld om overeen te komen met deze computer. Alles wat " +"alleen op de server is gewijzigd, wordt weggegooid." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." msgstr "" +"Deze computer is ingesteld om overeen te komen met de server Alles wat " +"alleen hier is gewijzigd, wordt weggegooid." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -16327,10 +16339,8 @@ msgid "Reset local to remote" msgstr "Lokaal opnieuw instellen vanuit server" #: GrampsWebSync/grampswebsync.py:712 -#, fuzzy -#| msgid "Learning" msgid "Warning:" -msgstr "Lerend" +msgstr "Waarschuwing:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16377,31 +16387,29 @@ msgid "Missing remotely" msgstr "Extern vermist" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file(s)" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" -msgstr[0] "%s mediabestand(en) downloaden" -msgstr[1] "%s mediabestand(en) downloaden" +msgstr[0] "%s mediabestand downloaden" +msgstr[1] "%s mediabestanden downloaden" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file(s)" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" -msgstr[0] "%s mediabestand(en) uploaden" -msgstr[1] "%s mediabestand(en) uploaden" +msgstr[0] "%s mediabestand uploaden" +msgstr[1] "%s mediabestanden uploaden" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "Probeer het opnieuw" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%s wijziging toegepast." +msgstr[1] "%s wijzigingen toegepast." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16413,44 +16421,41 @@ msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." msgstr[0] "" +"%s mediabestand ontbreekt aan beide kanten en kon niet worden overgedragen." msgstr[1] "" +"%s mediabestanden ontbreken aan beide kanten en konden niet worden " +"overgedragen." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "Beide stambomen zijn hetzelfde." +msgstr "Beide stambomen zijn gesynchroniseerd." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "%s mediabestanden succesvol gedownload." -msgstr[1] "%s mediabestanden succesvol gedownload." +msgstr[0] "%s mediabestand met succes gedownload." +msgstr[1] "%s mediabestanden met succes gedownload." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." -msgstr[0] "%s fouten opgetreden bij het downloaden." +msgstr[0] "%s fout opgetreden bij het downloaden." msgstr[1] "%s fouten opgetreden bij het downloaden." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "%s mediabestanden met succes geüploadet." +msgstr[0] "%s mediabestand met succes geüploadet." msgstr[1] "%s mediabestanden met succes geüploadet." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "%s fouten opgetreden bij het uploaden." +msgstr[0] "%s fout opgetreden bij het uploaden." msgstr[1] "%s fouten opgetreden bij het uploaden." #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 @@ -18780,7 +18785,7 @@ msgstr "Kon geen geldige morfologiepatronen genereren." #: NameSuite/name_processor/views/gramplet.py:51 msgid "Patronymic applied successfully!" -msgstr "Patroniem succesvol toegepast!" +msgstr "Patroniem met succes toegepast!" #: NameSuite/name_processor/views/gramplet.py:77 msgid "Apply Suggestion" @@ -21794,6 +21799,8 @@ msgid "" "\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " "database" msgstr "" +")\n" +"\tVereist BerkeleyDB of Python-bsddb3 Python adapter en de Berkeley database" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " @@ -25172,7 +25179,7 @@ msgstr "" #: WebSearch/WebSearch.py:2229 #, python-format msgid "Note #%(id)s has been successfully added" -msgstr "Notitie #%(id)s is met succes toegevoeg" +msgstr "Notitie #%(id)s is met succes toegevoegd" #: WebSearch/WebSearch.py:2232 msgid "Error creating note" @@ -25188,7 +25195,7 @@ msgstr "WebSearch Link" #: WebSearch/WebSearch.py:2428 msgid "Attribute has been successfully added" -msgstr "Attribuut is met success toegevoegd" +msgstr "Attribuut is met succes toegevoegd" #: WebSearch/WebSearch.py:2460 #, python-brace-format From a1a0ba0a0d1c5fb0bf8b845fc520e2b4c375c74a Mon Sep 17 00:00:00 2001 From: Pedro Albuquerque Date: Wed, 5 Aug 2026 20:02:19 +0200 Subject: [PATCH 118/156] Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (5586 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/pt_PT/ --- po/pt_PT.po | 73 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/po/pt_PT.po b/po/pt_PT.po index ee2a67c94..b1ef46848 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-07-30 22:02+0000\n" +"PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) \n" @@ -16005,12 +16005,16 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"Impossível utilizar o porta-chaves do sistema. O confinamento do Snap " +"bloqueia o acesso até que execute: %s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." msgstr "" +"Impossível utilizar o porta-chaves do sistema. Terá de inserir a sua senha " +"sempre que necessário." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -16066,11 +16070,13 @@ msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." msgstr "" +"A árvore genealógica foi alterada enquanto as alterações estavam a ser " +"revistas. Nenhuma alteração foi aplicada. Por favor, compare novamente." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "O servidor não conseguiu aplicar as alterações: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -16156,25 +16162,31 @@ msgstr "Erro:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "Modo de sincronização" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." msgstr "" +"As alterações de ambos os lados são combinadas. Os objectos editados em " +"ambos os locais são unidos." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." msgstr "" +"O servidor foi feito para corresponder a este computador. Qualquer alteração " +"feita no servidor é descartada." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." msgstr "" +"Este computador foi feito para corresponder ao servidor. Qualquer alteração " +"feita aqui é ignorada." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -16189,10 +16201,8 @@ msgid "Reset local to remote" msgstr "Repor a local com a remota" #: GrampsWebSync/grampswebsync.py:712 -#, fuzzy -#| msgid "Learning" msgid "Warning:" -msgstr "Aprendizagem" +msgstr "Aviso:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16239,31 +16249,29 @@ msgid "Missing remotely" msgstr "Remotos em falta" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file(s)" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" -msgstr[0] "A transferir %s ficheiro(s) multimédia" -msgstr[1] "A transferir %s ficheiro(s) multimédia" +msgstr[0] "A transferir %s ficheiro multimédia" +msgstr[1] "A transferir %s ficheiros multimédia" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file(s)" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" -msgstr[0] "A enviar %s ficheiro(s) multimédia" -msgstr[1] "A enviar %s ficheiro(s) multimédia" +msgstr[0] "A enviar %s ficheiro multimédia" +msgstr[1] "A enviar %s ficheiros multimédia" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "Tentar novamente" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Aplicar %s alteração." +msgstr[1] "Aplicar %s alterações." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16275,44 +16283,42 @@ msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." msgstr[0] "" +"O ficheiro multimédia %s não existe em nenhum dos lados e não foi possível " +"transferi-lo." msgstr[1] "" +"Os ficheiros multimédia %s não existem em nenhum dos lados e não foi " +"possível transferi-los." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "As árvores são idênticas." +msgstr "As árvores já estão sincronizadas." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "Transferiu com êxito %s ficheiros multimédia." +msgstr[0] "Transferiu com êxito %s ficheiro multimédia." msgstr[1] "Transferiu com êxito %s ficheiros multimédia." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." -msgstr[0] "Encontrados %s erros durante a transferência." +msgstr[0] "Encontrado %s erro durante a transferência." msgstr[1] "Encontrados %s erros durante a transferência." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "Envio com êxito de %s ficheiros multimédia." +msgstr[0] "Envio com êxito de %s ficheiro multimédia." msgstr[1] "Envio com êxito de %s ficheiros multimédia." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "%s erros encontrados durante o envio." +msgstr[0] "%s erro encontrado durante o envio." msgstr[1] "%s erros encontrados durante o envio." #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 @@ -21619,6 +21625,9 @@ msgid "" "\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " "database" msgstr "" +")\n" +"\tRequer adaptador Python BerkeleyDB ou Python-bsddb3 e a base de dados " +"Berkeley" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " From bbf12a214fe18731d1f08ae4db914b7b716a4a18 Mon Sep 17 00:00:00 2001 From: Milan Date: Wed, 5 Aug 2026 20:02:20 +0200 Subject: [PATCH 119/156] Translated using Weblate (Slovak) Currently translated at 98.7% (5514 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sk/ --- po/sk.po | 89 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/po/sk.po b/po/sk.po index a5aa0e0b4..9526c1176 100644 --- a/po/sk.po +++ b/po/sk.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-08-01 16:13+0000\n" +"PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak \n" @@ -16018,12 +16018,16 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"Systémovú kľúčenku sa nepodarilo použiť. Obmedzenie Snap blokuje prístup, " +"kým nespustíte: %s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." msgstr "" +"Systémovú kľúčenku sa nepodarilo použiť. Heslo budete musieť zadávať " +"zakaždým." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -16078,11 +16082,13 @@ msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." msgstr "" +"Rodinný strom bol modifikovaný počas kontroly zmien. Nič nebolo aplikované. " +"Prosím, porovnajte znova." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "Server nemohol aplikovať zmeny: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -16166,25 +16172,31 @@ msgstr "Chyba:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "Synchronizovaný režim" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." msgstr "" +"Zmeny z oboch strán sú kombinované. Objekty upravené na oboch miestach sa " +"zlúčia." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." msgstr "" +"Server sa upraví tak, aby zodpovedal tomuto počítaču. Čokoľvek zmenené len " +"na serveri sa vyradí." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." msgstr "" +"Tento počítač sa upraví tak, aby zodpovedal serveru. Čokoľvek zmenené len tu " +"sa vyradí." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -16199,10 +16211,8 @@ msgid "Reset local to remote" msgstr "Obnoviť lokálne na vzdialené" #: GrampsWebSync/grampswebsync.py:712 -#, fuzzy -#| msgid "Learning" msgid "Warning:" -msgstr "Učenie" +msgstr "Upozornenie:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16249,34 +16259,32 @@ msgid "Missing remotely" msgstr "Chýbajúce vzdialene" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file(s)" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" -msgstr[0] "Sťahovanie %s súboru(-ov) média" -msgstr[1] "Sťahovanie %s súboru(-ov) média" -msgstr[2] "Sťahovanie %s súboru(-ov) média" +msgstr[0] "Sťahovanie %s mediálneho súboru" +msgstr[1] "Sťahovanie %s mediálnych súborov" +msgstr[2] "Sťahovanie %s mediálnych súborov" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file(s)" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" -msgstr[0] "Nahrávanie %s súboru(-ov) média" -msgstr[1] "Nahrávanie %s súboru(-ov) média" -msgstr[2] "Nahrávanie %s súboru(-ov) média" +msgstr[0] "Nahrávanie %s mediálneho súboru" +msgstr[1] "Nahrávanie %s mediálnych súborov" +msgstr[2] "Nahrávanie %s mediálnych súborov" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "Skúste znova" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Aplikovaná %s zmena." +msgstr[1] "Aplikované %s zmeny." +msgstr[2] "Aplikovaných %s zmien." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16287,50 +16295,46 @@ msgstr "Mediálne súbory sú synchronizované." msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." -msgstr[0] "" +msgstr[0] "%s mediálny súbor chýba na oboch stranách a nepodarilo sa ho preniesť." msgstr[1] "" +"%s mediálne súbory chýbajú na oboch stranách a nepodarilo sa ich preniesť." msgstr[2] "" +"%s mediálnych súborov chýba na oboch stranách a nepodarilo sa ich preniesť." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "Obidva stromy sú rovnaké." +msgstr "Obidva stromy sú už synchronizované." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "Úspešne stiahnuté %s mediálne súbory." +msgstr[0] "Úspešne stiahnutý %s mediálny súbor." msgstr[1] "Úspešne stiahnuté %s mediálne súbory." -msgstr[2] "Úspešne stiahnuté %s mediálne súbory." +msgstr[2] "Úspešne stiahnutých %s mediálnych súborov." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." -msgstr[0] "Počas sťahovania sa vyskytlo %s chýb." -msgstr[1] "Počas sťahovania sa vyskytlo %s chýb." +msgstr[0] "Počas sťahovania sa vyskytla %s chyba." +msgstr[1] "Počas sťahovania sa vyskytli %s chyby." msgstr[2] "Počas sťahovania sa vyskytlo %s chýb." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "Úspešne nahraté %s mediálne súbory." -msgstr[1] "Úspešne nahraté %s mediálne súbory." -msgstr[2] "Úspešne nahraté %s mediálne súbory." +msgstr[0] "Úspešne nahraný %s mediálny súbor." +msgstr[1] "Úspešne nahrané %s mediálne súbory." +msgstr[2] "Úspešne nahraných %s mediálnych súborov." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "Počas nahrávania sa vyskytlo %s chýb." -msgstr[1] "Počas nahrávania sa vyskytlo %s chýb." +msgstr[0] "Počas nahrávania sa vyskytla %s chyba." +msgstr[1] "Počas nahrávania sa vyskytli %s chyby." msgstr[2] "Počas nahrávania sa vyskytlo %s chýb." #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 @@ -21621,6 +21625,9 @@ msgid "" "\tRequires BerkeleyDB or Python-bsddb3 Python adapter and the Berkeley " "database" msgstr "" +")\n" +"\tVyžaduje BerkeleyDB alebo adaptér Python-bsddb3 pre Python a databázu " +"Berkeley" #: PrerequisitesCheckerGramplet/PrerequisitesCheckerGramplet.py:837 msgid " • SQLite Database library (sqlite3: " From 64f47a66515251f4eda469de63a726d305ce1e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Ekholm?= Date: Wed, 5 Aug 2026 20:02:20 +0200 Subject: [PATCH 120/156] Translated using Weblate (Swedish) Currently translated at 67.5% (3776 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/sv/ --- po/sv.po | 110 ++++++++++++++++++++++++++----------------------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/po/sv.po b/po/sv.po index 3cc7a1d22..84c2b366a 100644 --- a/po/sv.po +++ b/po/sv.po @@ -18,7 +18,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-07-05 22:49+0000\n" +"PO-Revision-Date: 2026-08-03 04:02+0000\n" "Last-Translator: Pär Ekholm \n" "Language-Team: Swedish \n" @@ -27,7 +27,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.7.1.dev0\n" +"X-Generator: Weblate 2026.8.dev0\n" msgid "Birthdays" msgstr "Födelsedagar" @@ -36,28 +36,22 @@ msgid "Ignore birthdays with tag" msgstr "Ignorera födelsedagar med tagg" msgid "Month and day" -msgstr "" +msgstr "Månad och dag" msgid "Only show birthdays with tag" msgstr "Visa endast födelsedagar med tagg" msgid "Proximity to current date" -msgstr "" +msgstr "Närhet till aktuellt datum" -#, fuzzy -#| msgid "Sort by " msgid "Sort birthdays by" -msgstr "Sortera efter " +msgstr "Sortera födelsedagar efter" -#, fuzzy -#| msgid "Birth date of deceased" msgid "Sort dates of death by" -msgstr "Den avlidnes födelsedatum" +msgstr "Sortera dödsdatum efter" -#, fuzzy -#| msgid "a gramplet that displays the birthdays of the living people" msgid "a gramplet that displays death dates in sorted order" -msgstr "en Gramplet, som visar födelsedagarna för levande personer" +msgstr "en gramplet som visar dödsdatum i sorterad ordning" msgid "a gramplet that displays the birthdays of the living people" msgstr "en Gramplet, som visar födelsedagarna för levande personer" @@ -1321,10 +1315,8 @@ msgstr "" "-> Välj Hemperson." #: CombinedView/personpage.py:306 CombinedView/personpage.py:368 -#, fuzzy -#| msgid "Adding Tags to family (%s)" msgid "Add existing child to family" -msgstr "Lägger till taggar till familjen (%s)" +msgstr "Lägg till befintligt barn i familjen" #: CombinedView/personpage.py:597 #, python-format @@ -5438,7 +5430,7 @@ msgstr "Tillåt reguljära uttryck." #: ExcludeSubtreeFilter/excludesubtree.gpr.py:24 #: ExcludeSubtreeFilter/excludesubtree.py:106 msgid "People reachable from , stopping at matches" -msgstr "" +msgstr "Personer som kan nås från , stannar vid matchningar" #: ExcludeSubtreeFilter/excludesubtree.gpr.py:26 #: ExcludeSubtreeFilter/excludesubtree.py:109 @@ -5447,11 +5439,14 @@ msgid "" "and children of attached families, recursively) stopping at persons in " "." msgstr "" +"Matchar personer som är nåbara med början från (följer alla " +"föräldrar och barn till sammankopplade familjer, rekursivt) och stannar vid " +"personer i ." #: ExcludeSubtreeFilter/excludesubtree.py:121 #: FilterRules/isrelatedwithfiltermatch.py:80 msgid "Retrieving all sub-filter matches" -msgstr "" +msgstr "Hämtar alla underfilterträffar" #: ExportPersonGexf/exportpersongexf.gpr.py:30 msgid "Person GEXF" @@ -15920,12 +15915,16 @@ msgid "" "The system keyring could not be used. Snap confinement blocks access until " "you run: %s" msgstr "" +"Systemnyckelringen kunde inte användas. Snap-begränsning blockerar åtkomst " +"tills du kör: %s" #: GrampsWebSync/grampswebsync.py:128 msgid "" "The system keyring could not be used. You will need to enter your password " "each time." msgstr "" +"Systemnyckelringen kunde inte användas. Du måste ange ditt lösenord varje " +"gång." #: GrampsWebSync/grampswebsync.py:145 msgid "Authentication failed. Please check your username and password." @@ -15981,11 +15980,13 @@ msgid "" "The family tree was modified while the changes were being reviewed. Nothing " "has been applied. Please compare again." msgstr "" +"Släktträdet ändrades medan ändringarna granskades. Ingenting har tillämpats. " +"Jämför igen." #: GrampsWebSync/grampswebsync.py:175 #, python-format msgid "The server could not apply the changes: %s" -msgstr "" +msgstr "Servern kunde inte tillämpa ändringarna: %s" #: GrampsWebSync/grampswebsync.py:177 #, python-format @@ -16069,25 +16070,31 @@ msgstr "Fel:" #: GrampsWebSync/grampswebsync.py:646 msgid "Sync mode" -msgstr "" +msgstr "Synkroniseringsläge" #: GrampsWebSync/grampswebsync.py:656 msgid "" "Changes from both sides are combined. Objects edited in both places are " "merged." msgstr "" +"Förändringar från båda sidor kombineras. Objekt som redigerats på båda " +"ställena slås samman." #: GrampsWebSync/grampswebsync.py:660 msgid "" "The server is made to match this computer. Anything changed only on the " "server is discarded." msgstr "" +"Servern är gjord för att matcha den här datorn. Allt som ändras endast på " +"servern kasseras." #: GrampsWebSync/grampswebsync.py:664 msgid "" "This computer is made to match the server. Anything changed only here is " "discarded." msgstr "" +"Den här datorn är gjord för att matcha servern. Allt som ändras endast här " +"kasseras." #: GrampsWebSync/grampswebsync.py:671 msgid "Bidirectional Synchronization" @@ -16103,7 +16110,7 @@ msgstr "Återställ lokalt till fjärrläge" #: GrampsWebSync/grampswebsync.py:712 msgid "Warning:" -msgstr "" +msgstr "Varning:" #: GrampsWebSync/grampswebsync.py:722 msgid "Local changes" @@ -16150,33 +16157,29 @@ msgid "Missing remotely" msgstr "Saknas på distans" #: GrampsWebSync/grampswebsync.py:905 -#, fuzzy, python-format -#| msgid "Downloading %s media file" -#| msgid_plural "Downloading %s media files" +#, python-format msgid "Downloading %s media file" msgid_plural "Downloading %s media files" msgstr[0] "Laddar ner %s mediefil" -msgstr[1] "Laddar ner %s mediefil" +msgstr[1] "Laddar ner %s mediefiler" #: GrampsWebSync/grampswebsync.py:920 -#, fuzzy, python-format -#| msgid "Uploading %s media file" -#| msgid_plural "Uploading %s media files" +#, python-format msgid "Uploading %s media file" msgid_plural "Uploading %s media files" msgstr[0] "Laddar upp %s mediefil" -msgstr[1] "Laddar upp %s mediefil" +msgstr[1] "Laddar upp %s mediefiler" #: GrampsWebSync/grampswebsync.py:965 msgid "Try again" -msgstr "" +msgstr "Försök igen" #: GrampsWebSync/grampswebsync.py:999 #, python-format msgid "Applied %s change." msgid_plural "Applied %s changes." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Tillämpade %s ändring." +msgstr[1] "Tillämpade %s ändringar." #: GrampsWebSync/grampswebsync.py:1006 msgid "Media files are in sync." @@ -16187,47 +16190,40 @@ msgstr "Mediefiler är synkroniserade." msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%s mediafil saknas på båda sidor och kunde inte överföras." +msgstr[1] "%s mediafiler saknas på båda sidor och kunde inte överföras." #: GrampsWebSync/grampswebsync.py:1020 -#, fuzzy -#| msgid "Both trees are the same." msgid "Both trees are already in sync." -msgstr "Båda träden är likadana." +msgstr "Båda träden är redan synkroniserade." #: GrampsWebSync/grampswebsync.py:1033 -#, fuzzy, python-format -#| msgid "Successfully downloaded %s media files." +#, python-format msgid "Successfully downloaded %s media file." msgid_plural "Successfully downloaded %s media files." -msgstr[0] "Laddar ner %s mediefiler med lyckat resultat." -msgstr[1] "Laddar ner %s mediefiler med lyckat resultat." +msgstr[0] "Laddade ner %s mediefil med lyckat resultat." +msgstr[1] "Laddade ner %s mediefiler med lyckat resultat." #: GrampsWebSync/grampswebsync.py:1043 -#, fuzzy, python-format -#| msgid "Encountered %s errors during download." +#, python-format msgid "Encountered %s error during download." msgid_plural "Encountered %s errors during download." msgstr[0] "%s fel uppstod under nedladdningen." msgstr[1] "%s fel uppstod under nedladdningen." #: GrampsWebSync/grampswebsync.py:1056 -#, fuzzy, python-format -#| msgid "Successfully uploaded %s media files." +#, python-format msgid "Successfully uploaded %s media file." msgid_plural "Successfully uploaded %s media files." -msgstr[0] "Laddar upp %s mediefiler med lyckat resultat." -msgstr[1] "Laddar upp %s mediefiler med lyckat resultat." +msgstr[0] "Laddade upp %s mediefil med lyckat resultat." +msgstr[1] "Laddade upp %s mediefiler med lyckat resultat." #: GrampsWebSync/grampswebsync.py:1065 -#, fuzzy, python-format -#| msgid "Encountered %s error during upload." -#| msgid_plural "Encountered %s errors during upload." +#, python-format msgid "Encountered %s error during upload." msgid_plural "Encountered %s errors during upload." -msgstr[0] "Mötte %s fel vid uppladdning." -msgstr[1] "Mötte %s fel vid uppladdning." +msgstr[0] "%s fel uppstod under uppladdningen." +msgstr[1] "%s fel uppstod under uppladdningen." #: GrampyScript/GrampyScript.gpr.py:23 GrampyScript/GrampyScript.gpr.py:32 msgid "Gram.py Script" @@ -16768,14 +16764,12 @@ msgid "Families with a tag containing " msgstr "Familjer med en tagg som innehåller " #: HasTagSubstr/hastagsubstr.gpr.py:42 HasTagSubstr/hastagsubstr.py:104 -#, fuzzy -#| msgid "Matches families that are matched by an event filter" msgid "Matches families with a tag whose name contains the given substring" -msgstr "Matchar familjer som matchas av ett händelsefilter" +msgstr "Matchar familjer med en tagg vars namn innehåller den givna delsträngen" #: HasTagSubstr/hastagsubstr.gpr.py:57 HasTagSubstr/hastagsubstr.py:113 msgid "Events with a tag containing " -msgstr "" +msgstr "Händelser med en tagg som innehåller " #: HasTagSubstr/hastagsubstr.gpr.py:59 HasTagSubstr/hastagsubstr.py:115 msgid "Matches events with a tag whose name contains the given substring" @@ -16794,10 +16788,8 @@ msgid "Sources with a tag containing " msgstr "" #: HasTagSubstr/hastagsubstr.gpr.py:93 HasTagSubstr/hastagsubstr.py:137 -#, fuzzy -#| msgid "Matches Sources with values containing the chosen parameters" msgid "Matches sources with a tag whose name contains the given substring" -msgstr "Matchar källor med värden som innehåller de valda parametrarna" +msgstr "Matchar källor med en tagg vars namn innehåller den angivna delsträngen" #: HasTagSubstr/hastagsubstr.gpr.py:108 HasTagSubstr/hastagsubstr.py:146 msgid "Citations with a tag containing " From 64451cfca401e68fec64cc50e616b3cb026f93f7 Mon Sep 17 00:00:00 2001 From: Kaj Arne Mikkelsen Date: Wed, 5 Aug 2026 20:02:20 +0200 Subject: [PATCH 121/156] Translated using Weblate (Danish) Currently translated at 58.6% (3277 of 5586 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ --- po/da.po | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/po/da.po b/po/da.po index 279e15265..42bafc9bc 100644 --- a/po/da.po +++ b/po/da.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-01 09:38-0700\n" -"PO-Revision-Date: 2026-08-01 16:13+0000\n" +"PO-Revision-Date: 2026-08-04 10:13+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish \n" @@ -20,7 +20,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.8.dev0\n" +"X-Generator: Weblate 2026.8.1.dev0\n" msgid "Birthdays" msgstr "Fødseldage" @@ -1475,8 +1475,7 @@ msgstr "" "bibliotek." #: D3Charts/DescendantIndentedTree.py:678 -#, fuzzy, python-format -#| msgid "See %(reference)s : %(spouse)s" +#, python-format msgctxt "spouse" msgid "See %(reference)s : %(spouse)s" msgstr "Se %(reference)s : %(spouse)s" @@ -1759,7 +1758,6 @@ msgid "The font size in pixels for biography body text." msgstr "Font størrelse i pixels for biografi body tekst." #: DEWebConnectPack/DEWebPack.gpr.py:11 -#, fuzzy msgid "DE Web Connect Pack" msgstr "DE Web Connect Pack" @@ -1782,14 +1780,11 @@ msgid "Google Archives" msgstr "Google arkiv" #: DEWebConnectPack/DEWebPack.py:43 -#, fuzzy -#| msgid "GoogleEarth" msgid "DE Google" msgstr "DE Google" #: DEWebConnectPack/DEWebPack.py:44 UKWebConnectPack/UKWebPack.py:39 #: USWebConnectPack/USWebPack.py:42 -#, fuzzy msgid "Open Library" msgstr "Open Library" @@ -1798,7 +1793,6 @@ msgid "Surname map (1890-1996)" msgstr "Efternavnskort (1890-1996)" #: DEWebConnectPack/DEWebPack.py:46 -#, fuzzy msgid "GenWiki" msgstr "GenWiki" @@ -1858,7 +1852,7 @@ msgid " and ends at " msgstr " og slutter ved " #: DNA/dnasegmentmap.py:1079 DNA/dnasegmentmap.py:1082 -#, fuzzy, python-brace-format +#, python-brace-format msgid "{0}" msgstr "{0}" @@ -2206,7 +2200,7 @@ msgstr "Fejl: ugyldig dato i det første udtryk" #: DateCalculator/DateCalculator.py:169 msgid "Error: invalid offset for second expression" -msgstr "" +msgstr "Fejl: ugyldig afstand for andet udtryk" #: DateCalculator/DateCalculator.py:195 DateCalculator/DateCalculator.py:197 #: DateCalculator/DateCalculator.py:205 DateCalculator/DateCalculator.py:207 @@ -2215,7 +2209,7 @@ msgstr "Fejl: Mindst et udtryk skal være en dato" #: DateCalculator/DateCalculator.py:223 msgid "Enter an expression in the entries above and click Calculate." -msgstr "" +msgstr "Indtast et udtryk i ovenstående poster og klik på Beregn." #: DeepConnectionsGramplet/DeepConnectionsGramplet.gpr.py:4 msgid "Deep Connections Gramplet" @@ -2230,48 +2224,36 @@ msgid "Deep Connections" msgstr "Dybe Slægtskaber" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:106 -#, fuzzy -#| msgid "Pause" msgid "⏸ Pause" -msgstr "Pause" +msgstr "⏸ Pause" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:108 msgid "Pause the current search" -msgstr "" +msgstr "Sæt den nuværende søgning på pause" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:111 msgid "▶ Continue" -msgstr "" +msgstr "▶ Fortsæt" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:114 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Continue searching for more relations" -msgstr "" -"Pauset.\n" -"Tryk Fortsæt for at søge efter flere slægtskaber.\n" +msgstr "Tryk Fortsæt for at søge efter flere slægtskaber" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:118 -#, fuzzy -#| msgid "Copy" msgid "📋 Copy" -msgstr "Kopier" +msgstr "📋 Kopiér" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:120 -#, fuzzy -#| msgid "Collections Clipboard" msgid "Copy selected people to clipboard" -msgstr "Samlings Udklipsholder" +msgstr "Kopiér valgte personer til udklipsholderen" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:128 msgid "🗑 Clear" -msgstr "" +msgstr "🗑 Nulstil" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:129 msgid "Clear all results and reset" -msgstr "" +msgstr "Fjern alle resultater og nulstil" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:158 #, fuzzy From 7eff79b4d77561d9a774fc189d1c7caea112ede8 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Aug 2026 20:02:21 +0200 Subject: [PATCH 122/156] Update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/ --- po/ar.po | 249 +++++++++++++++++++++++++++++++++++++++++++- po/bg.po | 254 ++++++++++++++++++++++++++++++++++++++++++++- po/ca.po | 272 ++++++++++++++++++++++++++++++++++++++++++++++++- po/cs.po | 267 +++++++++++++++++++++++++++++++++++++++++++++++- po/cy.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/da.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/de.po | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++- po/el.po | 249 +++++++++++++++++++++++++++++++++++++++++++- po/en_GB.po | 258 +++++++++++++++++++++++++++++++++++++++++++++- po/eo.po | 251 ++++++++++++++++++++++++++++++++++++++++++++- po/es.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/fi.po | 283 +++++++++++++++++++++++++++++++++++++++++++++++++- po/fr.po | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++- po/he.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/hr.po | 283 +++++++++++++++++++++++++++++++++++++++++++++++++- po/hu.po | 267 +++++++++++++++++++++++++++++++++++++++++++++++- po/is.po | 250 ++++++++++++++++++++++++++++++++++++++++++++- po/it.po | 269 +++++++++++++++++++++++++++++++++++++++++++++++- po/ja.po | 269 +++++++++++++++++++++++++++++++++++++++++++++++- po/ka.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/ln.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/lt.po | 266 ++++++++++++++++++++++++++++++++++++++++++++++- po/lv.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/mn.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/nb.po | 270 +++++++++++++++++++++++++++++++++++++++++++++++- po/ne.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/nl.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/nn.po | 255 +++++++++++++++++++++++++++++++++++++++++++++- po/oc.po | 247 +++++++++++++++++++++++++++++++++++++++++++- po/pl.po | 277 ++++++++++++++++++++++++++++++++++++++++++++++++- po/pt_BR.po | 263 ++++++++++++++++++++++++++++++++++++++++++++++- po/pt_PT.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/ru.po | 281 +++++++++++++++++++++++++++++++++++++++++++++++++- po/sk.po | 283 +++++++++++++++++++++++++++++++++++++++++++++++++- po/sl.po | 252 ++++++++++++++++++++++++++++++++++++++++++++- po/sq.po | 252 ++++++++++++++++++++++++++++++++++++++++++++- po/sr.po | 249 +++++++++++++++++++++++++++++++++++++++++++- po/sv.po | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/tr.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/uk.po | 280 +++++++++++++++++++++++++++++++++++++++++++++++++- po/vi.po | 252 ++++++++++++++++++++++++++++++++++++++++++++- po/zh_CN.po | 254 ++++++++++++++++++++++++++++++++++++++++++++- po/zh_HK.po | 249 +++++++++++++++++++++++++++++++++++++++++++- po/zh_TW.po | 249 +++++++++++++++++++++++++++++++++++++++++++- 44 files changed, 11556 insertions(+), 65 deletions(-) diff --git a/po/ar.po b/po/ar.po index fdee3505a..777e6ef98 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps-4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2014-06-29 15:50+0300\n" "Last-Translator: Munzir Taha (منذر طه) \n" "Language-Team: Arabic <>\n" @@ -14924,6 +14924,253 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "فحص عناوين المكان" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/bg.po b/po/bg.po index 1dec47eeb..c21074ec9 100644 --- a/po/bg.po +++ b/po/bg.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-03-14 05:09+0000\n" "Last-Translator: Iskren Petkov \n" "Language-Team: Bulgarian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/ca.po b/po/ca.po index ead8c7f1b..f6002d2ae 100644 --- a/po/ca.po +++ b/po/ca.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: ca\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-09-03 03:01+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Catalan " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Famílies que coincideixen amb " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Paràmetres de coincidència de la font" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/cs.po b/po/cs.po index f7f37e60e..835f512d4 100644 --- a/po/cs.po +++ b/po/cs.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.2.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-29 14:56+0000\n" "Last-Translator: Milan \n" "Language-Team: Czech " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "No matching surname." +msgid "Sources matching the " +msgstr "Žádné odpovídající příjmení" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/cy.po b/po/cy.po index b3a87decc..8340fe287 100644 --- a/po/cy.po +++ b/po/cy.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -14912,6 +14912,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/da.po b/po/da.po index 42bafc9bc..b295322f4 100644 --- a/po/da.po +++ b/po/da.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-04 10:13+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familier der matcher " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Kilde der matcher parametre" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Tillad regular expressions." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Eksporter GEDCOM udvidelser (GED2)" diff --git a/po/de.po b/po/de.po index 56af13a84..43dd860e5 100644 --- a/po/de.po +++ b/po/de.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Mirko Leonhäuser \n" "Language-Team: German " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familien, die entsprechen" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Quelle, die den Parametern entspricht" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "Regulärer Ausdruck" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "GEDCOM- Export-Erweiterungen (GED2)" @@ -26714,9 +26994,6 @@ msgstr "" #~ msgid "Sort all children in birth order" #~ msgstr "Alle Kinder in Reihenfolge der Geburt sortieren" -#~ msgid "Person Filter Editor" -#~ msgstr "Personenfiltereditor" - #~ msgid "Style" #~ msgstr "Stil" diff --git a/po/el.po b/po/el.po index ef5e3deec..112fe4bdd 100644 --- a/po/el.po +++ b/po/el.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.0.3.\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-12-23 02:37+0000\n" "Last-Translator: klak kloyk \n" "Language-Team: Greek " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/en_GB.po b/po/en_GB.po index ec4eff8f2..db39115e9 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -28,7 +28,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.5.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-11 13:01+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom) " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/eo.po b/po/eo.po index 0dc8a6cb3..46dac874f 100644 --- a/po/eo.po +++ b/po/eo.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-08-29 20:30+0000\n" "Last-Translator: jmichault \n" "Language-Team: Esperanto " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/es.po b/po/es.po index 3296fc2a5..bca69f5e3 100644 --- a/po/es.po +++ b/po/es.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-24 05:14+0000\n" "Last-Translator: Juan Saavedra \n" "Language-Team: Spanish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familias coincidiendo con " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Origen coincide con parámetros" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Concede expresiones regulares." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Exportar Extensiones de GEDCOM (GED2)" diff --git a/po/fi.po b/po/fi.po index baf8f44c5..0cd1f57ea 100644 --- a/po/fi.po +++ b/po/fi.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: fi\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-26 17:40+0000\n" "Last-Translator: Juha Mäkeläinen \n" "Language-Team: Finnish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Suotimen mukaiset perheet" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Lähteet valitulla arvolla" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Salli säännölliset lausekkeet." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Laajennettu GEDCOM vienti (GED2)" @@ -26269,8 +26547,5 @@ msgstr "" #~ msgid "Detailed Info" #~ msgstr "Lisätietoja" -#~ msgid "Person Filter Editor" -#~ msgstr "Henkilösuodinmuokkain" - #~ msgid "Parents:" #~ msgstr "Vanhemmat:" diff --git a/po/fr.po b/po/fr.po index d3d698f7a..e21b020b8 100644 --- a/po/fr.po +++ b/po/fr.po @@ -39,7 +39,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-01 15:01+0000\n" "Last-Translator: \"David D.\" \n" "Language-Team: French " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familles correspondant à " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Source correspondantes" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Autorise les expressions régulières." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Exportation des extensions Gedcom (GED2)" @@ -26664,9 +26944,6 @@ msgstr "" #~ msgid "Given" #~ msgstr "Prénom" -#~ msgid "Person Filter Editor" -#~ msgstr "Éditeur de filtre sur l'individu" - #~ msgid "Parents:" #~ msgstr "Parents :" diff --git a/po/he.po b/po/he.po index f34cc38cf..bb94a4932 100644 --- a/po/he.po +++ b/po/he.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.2.0 – mediamerge\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 20:31+0000\n" "Last-Translator: Avi Markovitz \n" "Language-Team: Hebrew " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "משפחות שתואמות <מסנן אירוע>" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "מדדי התאמת מקורות" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "ביטוי רגולרי" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "ייצוא הרחבות GEDCOM‏ (GED2)" diff --git a/po/hr.po b/po/hr.po index b8ef064af..91f697c2c 100644 --- a/po/hr.po +++ b/po/hr.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-05-17 15:49+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: Croatian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Obitelji koje odgavaraju " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Izvor odgovara parametrima" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Dozvoli regularne izraze." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Izvezi datoteke GEDCOM sufiksa (GED2)" @@ -26351,6 +26629,3 @@ msgstr "" #~ msgid "label" #~ msgstr "etiketa" - -#~ msgid "Person Filter Editor" -#~ msgstr "Uređivač filtra za osobe" diff --git a/po/hu.po b/po/hu.po index a8c7809a3..94d238e94 100644 --- a/po/hu.po +++ b/po/hu.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: hu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-05 13:54+0000\n" "Last-Translator: Milan \n" "Language-Team: Hungarian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "No matching surname." +msgid "Sources matching the " +msgstr "Nem található megfelelő vezetéknév." + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/is.po b/po/is.po index ab2a13e2b..59b2bc9bf 100644 --- a/po/is.po +++ b/po/is.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-04-30 20:09+0000\n" "Last-Translator: Sveinn í Felli \n" "Language-Team: Icelandic " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/it.po b/po/it.po index 0535b0f9a..d67ef33ad 100644 --- a/po/it.po +++ b/po/it.po @@ -67,7 +67,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-31 16:24+0000\n" "Last-Translator: medardo \n" "Language-Team: Italian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Permette le espressioni reegolari." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/ja.po b/po/ja.po index 610a2361c..fcde8452d 100644 --- a/po/ja.po +++ b/po/ja.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 3.3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-03-24 10:31+0000\n" "Last-Translator: coolz daddy \n" "Language-Team: Japanese " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr " に一致する家族" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "ソース照合パラメータ" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "正規表現を許可します。" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/ka.po b/po/ka.po index 75037add5..319f30d62 100644 --- a/po/ka.po +++ b/po/ka.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-09-13 09:49+0000\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/ln.po b/po/ln.po index 24bc6358c..67024968c 100644 --- a/po/ln.po +++ b/po/ln.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -14911,6 +14911,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/lt.po b/po/lt.po index 9994477d1..f6c6a937a 100644 --- a/po/lt.po +++ b/po/lt.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: lt\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-01-04 20:01+0000\n" "Last-Translator: openSUSE Lietuviškai \n" "Language-Team: Lithuanian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/lv.po b/po/lv.po index 207fc5b3c..03c9416b9 100644 --- a/po/lv.po +++ b/po/lv.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -14912,6 +14912,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/mn.po b/po/mn.po index 025c3cf1c..54dbae915 100644 --- a/po/mn.po +++ b/po/mn.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-04-07 13:46+0000\n" "Last-Translator: \"Batsaihan P.\" \n" "Language-Team: Mongolian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/nb.po b/po/nb.po index 35320646f..27adc8e1d 100644 --- a/po/nb.po +++ b/po/nb.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: nb\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-01 12:35+0000\n" "Last-Translator: Harald Herreros \n" "Language-Team: Norwegian Bokmål " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familier som matcher " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Eksporter GEDCOM utvidelser (GED2)" diff --git a/po/ne.po b/po/ne.po index 448b37689..f468c5c1f 100644 --- a/po/ne.po +++ b/po/ne.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -14911,6 +14911,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/nl.po b/po/nl.po index c924e3e7f..e2c17409b 100644 --- a/po/nl.po +++ b/po/nl.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: MediaMerge 5.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Stephan Paternotte \n" "Language-Team: Dutch " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Gezinnen die overeenkomen met " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Met bron overeenkomende parameters" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "Reguliere expressies" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "GEDCOM-extensies exporteren (GED2)" diff --git a/po/nn.po b/po/nn.po index 03eb08b5e..41f8df2c8 100644 --- a/po/nn.po +++ b/po/nn.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: nn\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2014-12-22 21:26+0100\n" "Last-Translator: \n" "Language-Team: Norwegian Nynorsk \n" @@ -15036,6 +15036,259 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +#, fuzzy +#| msgid "Description: " +msgid "Person GOQL Filter" +msgstr "Omtale: " + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "Kontrollerer stadnamn" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +#, fuzzy +#| msgid "Description: " +msgid "Citation GOQL Filter" +msgstr "Omtale: " + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +#, fuzzy +#| msgid "Media Object Title" +msgid "Media GOQL Filter" +msgstr "Tittel på mediaobjekt" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/oc.po b/po/oc.po index dd4c8f85e..2cccb3025 100644 --- a/po/oc.po +++ b/po/oc.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -14911,6 +14911,251 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +msgid "Place GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/pl.po b/po/pl.po index 5d1d42b0a..c0e9ad9c2 100644 --- a/po/pl.po +++ b/po/pl.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-12-14 21:00+0000\n" "Last-Translator: WaldiS \n" "Language-Team: Polish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Rodziny pasujące do " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Źródło dopasowujące parametry" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Zezwala na używanie wyrażeń regularnych." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Eksport rozszerzeń GEDCOM (GED2)" diff --git a/po/pt_BR.po b/po/pt_BR.po index 1cd1ae1f2..8ab92a0be 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: trunk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Andre Magri \n" "Language-Team: Portuguese (Brazil) " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/pt_PT.po b/po/pt_PT.po index b1ef46848..5a7a08dd5 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps51\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Portuguese (Portugal) " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Famílias com " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Parâmetros de comparação de fontes" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "Expressão regular" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Exportar extensões GEDCOM (GED2)" diff --git a/po/ru.po b/po/ru.po index a75706c8f..77b856ef2 100644 --- a/po/ru.po +++ b/po/ru.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps50\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-09 08:01+0000\n" "Last-Translator: Vadim Barsukov \n" "Language-Team: Russian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Семьи подходящие под <фильтр событий>" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Источник с параметрами" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Экспорт в GEDCOM (GED2)" @@ -26863,9 +27139,6 @@ msgstr "" #~ msgid "Detailed Info" #~ msgstr "Подробная информация" -#~ msgid "Person Filter Editor" -#~ msgstr "Редактор фильтров людей" - #~ msgid "OsmGpsMap module not loaded." #~ msgstr "Модуль OsmGpsMap незагружен." diff --git a/po/sk.po b/po/sk.po index 9526c1176..cef58d21e 100644 --- a/po/sk.po +++ b/po/sk.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.1.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 04:01+0000\n" "Last-Translator: Milan \n" "Language-Team: Slovak " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Rodiny, ktoré vyhovujú filtru " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Parametre pre vyhľadávanie zdrojov" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "Regulárny výraz" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Export rozšíření GEDCOM (GED2)" @@ -16295,7 +16573,8 @@ msgstr "Mediálne súbory sú synchronizované." msgid "%s media file is missing on both sides and could not be transferred." msgid_plural "" "%s media files are missing on both sides and could not be transferred." -msgstr[0] "%s mediálny súbor chýba na oboch stranách a nepodarilo sa ho preniesť." +msgstr[0] "" +"%s mediálny súbor chýba na oboch stranách a nepodarilo sa ho preniesť." msgstr[1] "" "%s mediálne súbory chýbajú na oboch stranách a nepodarilo sa ich preniesť." msgstr[2] "" diff --git a/po/sl.po b/po/sl.po index 324583079..a04466c0c 100644 --- a/po/sl.po +++ b/po/sl.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: gramps 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2008-02-22 23:19+0100\n" "Last-Translator: Bernard Banko \n" "Language-Team: lugos slovenizacija \n" @@ -15060,6 +15060,256 @@ msgstr "" msgid "Select Form" msgstr "Izberi opombo" +#: GOQLFilter/goql.gpr.py:31 +#, fuzzy +msgid "Person GOQL Filter" +msgstr "V sorodu" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "Preverjanje nazivov krajev" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +#, fuzzy +msgid "Citation GOQL Filter" +msgstr "V sorodu" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +#, fuzzy +msgid "Media GOQL Filter" +msgstr "Filtri za predmete" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/sq.po b/po/sq.po index 179066d25..a8c0a2870 100644 --- a/po/sq.po +++ b/po/sq.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2008-11-13 21:00+0100\n" "Last-Translator: Vlora Jakupi \n" "Language-Team: \n" @@ -15026,6 +15026,256 @@ msgstr "" msgid "Select Form" msgstr "Përzgjedh shënim" +#: GOQLFilter/goql.gpr.py:31 +#, fuzzy +msgid "Person GOQL Filter" +msgstr "Të lidhur" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "Kontrrollimi i titullit të vendit" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +#, fuzzy +msgid "Citation GOQL Filter" +msgstr "Të lidhur" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +#, fuzzy +msgid "Media GOQL Filter" +msgstr "Filtër i Media Objektit" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/sr.po b/po/sr.po index 610ca8be7..2577d2789 100644 --- a/po/sr.po +++ b/po/sr.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-05-12 12:11+0000\n" "Last-Translator: Ранко Николић \n" "Language-Team: Serbian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/sv.po b/po/sv.po index 84c2b366a..4684276c0 100644 --- a/po/sv.po +++ b/po/sv.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-08-03 04:02+0000\n" "Last-Translator: Pär Ekholm \n" "Language-Team: Swedish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Familjer som matchar " + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Parametrar för källmatchning" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Tillåt reguljära uttryck." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Exportera GEDCOM-tillägg (GED2)" @@ -16765,7 +17043,8 @@ msgstr "Familjer med en tagg som innehåller " #: HasTagSubstr/hastagsubstr.gpr.py:42 HasTagSubstr/hastagsubstr.py:104 msgid "Matches families with a tag whose name contains the given substring" -msgstr "Matchar familjer med en tagg vars namn innehåller den givna delsträngen" +msgstr "" +"Matchar familjer med en tagg vars namn innehåller den givna delsträngen" #: HasTagSubstr/hastagsubstr.gpr.py:57 HasTagSubstr/hastagsubstr.py:113 msgid "Events with a tag containing " @@ -16789,7 +17068,8 @@ msgstr "" #: HasTagSubstr/hastagsubstr.gpr.py:93 HasTagSubstr/hastagsubstr.py:137 msgid "Matches sources with a tag whose name contains the given substring" -msgstr "Matchar källor med en tagg vars namn innehåller den angivna delsträngen" +msgstr "" +"Matchar källor med en tagg vars namn innehåller den angivna delsträngen" #: HasTagSubstr/hastagsubstr.gpr.py:108 HasTagSubstr/hastagsubstr.py:146 msgid "Citations with a tag containing " @@ -26305,8 +26585,5 @@ msgstr "" #~ msgid "Stable" #~ msgstr "Stabil" -#~ msgid "Person Filter Editor" -#~ msgstr "Redigera personfilter" - #~ msgid "Edit Note" #~ msgstr "Redigera notis" diff --git a/po/tr.po b/po/tr.po index 429b9de06..746ba109d 100644 --- a/po/tr.po +++ b/po/tr.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: 4.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-07-30 22:02+0000\n" "Last-Translator: Osman Öz \n" "Language-Team: Turkish " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr " ile eşleşen aileler" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Kaynak eşleştirme parametreleri" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Regular Expression" +msgid "GOQL expression" +msgstr "Normal İfade" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "GEDCOM Uzantılarını (GED2) Dışa Aktar" diff --git a/po/uk.po b/po/uk.po index 522507ac0..d04208a08 100644 --- a/po/uk.po +++ b/po/uk.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2025-11-17 06:51+0000\n" "Last-Translator: Fedir Zinchuk \n" "Language-Team: Ukrainian " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +#, fuzzy +#| msgid "Families matching " +msgid "Families matching the " +msgstr "Сім'ї, що відповідають <фільтру подій>" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +#, fuzzy +#| msgid "Source matching parameters" +msgid "Sources matching the " +msgstr "Параметри зіставлення джерела" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +#, fuzzy +#| msgid "Allow regular expressions." +msgid "GOQL expression" +msgstr "Дозволити використання регулярних виразів." + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "Експортувати розширення GEDCOM (GED2)" diff --git a/po/vi.po b/po/vi.po index 54d57f39e..f914c1efb 100644 --- a/po/vi.po +++ b/po/vi.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VIETNAMESE 4.2.8\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-02-08 09:09+0000\n" "Last-Translator: Securitocat \n" "Language-Team: Vietnamese " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 65f5b0f86..a5ff44290 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: GRAMPS VERSION 3.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2026-06-28 22:01+0000\n" "Last-Translator: Tian Shixiong \n" "Language-Team: Chinese (Simplified Han script) " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/zh_HK.po b/po/zh_HK.po index 46f30b3a7..81cac15fb 100644 --- a/po/zh_HK.po +++ b/po/zh_HK.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (Hong Kong) <(nothing)>\n" @@ -14924,6 +14924,253 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "檢查地點名稱" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" diff --git a/po/zh_TW.po b/po/zh_TW.po index 0ede4fcde..d095c6f5e 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Gramps 4.2.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 09:38-0700\n" +"POT-Creation-Date: 2026-08-04 08:55-0700\n" "PO-Revision-Date: 2015-03-18 17:31-0600\n" "Last-Translator: Anthony Fok \n" "Language-Team: Chinese (traditional) \n" @@ -14924,6 +14924,253 @@ msgstr "" msgid "Select Form" msgstr "" +#: GOQLFilter/goql.gpr.py:31 +msgid "Person GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:32 +msgid "Gramplet providing a gramps-object-query-language person filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:39 GOQLFilter/goql.gpr.py:58 +#: GOQLFilter/goql.gpr.py:77 GOQLFilter/goql.gpr.py:96 +#: GOQLFilter/goql.gpr.py:117 GOQLFilter/goql.gpr.py:136 +#: GOQLFilter/goql.gpr.py:155 GOQLFilter/goql.gpr.py:174 +#: GOQLFilter/goql.gpr.py:193 +msgid "GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:50 +msgid "Family GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:51 +msgid "Gramplet providing a gramps-object-query-language family filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:69 +msgid "Event GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:70 +msgid "Gramplet providing a gramps-object-query-language event filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:88 +#, fuzzy +#| msgid "Checking Place Titles" +msgid "Place GOQL Filter" +msgstr "檢查地點名稱" + +#: GOQLFilter/goql.gpr.py:89 +msgid "Gramplet providing a gramps-object-query-language place filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:107 +msgid "Repository GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:109 +msgid "Gramplet providing a gramps-object-query-language repository filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:128 +msgid "Source GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:129 +msgid "Gramplet providing a gramps-object-query-language source filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:147 +msgid "Citation GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:148 +msgid "Gramplet providing a gramps-object-query-language citation filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:166 +msgid "Media GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:167 +msgid "Gramplet providing a gramps-object-query-language media filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:185 +msgid "Note GOQL Filter" +msgstr "" + +#: GOQLFilter/goql.gpr.py:186 +msgid "Gramplet providing a gramps-object-query-language note filter" +msgstr "" + +#: GOQLFilter/goql.py:186 +msgid "A gramps-object-query-language where-expression, e.g.\n" +msgstr "" + +#: GOQLFilter/goql.py:188 +msgid "Enter inserts a newline; Ctrl+Enter runs Find.\n" +msgstr "" + +#: GOQLFilter/goql.py:189 +msgid "Up/Down at the first/last line recalls previous expressions.\n" +msgstr "" + +#: GOQLFilter/goql.py:190 +msgid "Tab always completes -- it never inserts a tab character." +msgstr "" + +#: GOQLFilter/goql.py:213 +#, python-format +msgid "%s filter" +msgstr "" + +#: GOQLFilter/goql.py:233 +msgid "This resets the filter parameters to empty state." +msgstr "" + +#: GOQLFilter/goql.py:239 +msgid "This opens a dialog to save the current expression as a named filter." +msgstr "" + +#: GOQLFilter/goql.py:244 +msgid "Open this gramplet's help page in a browser." +msgstr "" + +#: GOQLFilter/goql.py:402 +msgid "SQL" +msgstr "" + +#: GOQLFilter/goql.py:403 +msgid "Python evaluation" +msgstr "" + +#: GOQLFilter/goql.py:523 +#, python-format +msgid "Error applying filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:526 +#, python-format +msgid "Showing %(shown)d of %(total)d (%(method)s)" +msgstr "" + +#: GOQLFilter/goql.py:541 +#, python-format +msgid "Error resetting filter: %s" +msgstr "" + +#: GOQLFilter/goql.py:548 +msgid "Enter an expression first" +msgstr "" + +#: GOQLFilter/goql.py:560 +#, python-brace-format +msgid "Created by the GOQL Filter gramplet on {today}" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:30 GOQLFilter/whereexprrule.py:261 +msgid "People matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:32 +msgid "" +"Matches people for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:50 GOQLFilter/whereexprrule.py:266 +msgid "Families matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:52 +msgid "" +"Matches families for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:70 GOQLFilter/whereexprrule.py:271 +msgid "Events matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:72 +msgid "" +"Matches events for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:90 GOQLFilter/whereexprrule.py:276 +msgid "Places matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:92 +msgid "" +"Matches places for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:110 GOQLFilter/whereexprrule.py:281 +msgid "Repositories matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:112 +msgid "" +"Matches repositories for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:130 GOQLFilter/whereexprrule.py:286 +msgid "Sources matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:132 +msgid "" +"Matches sources for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:150 GOQLFilter/whereexprrule.py:291 +msgid "Citations matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:152 +msgid "" +"Matches citations for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:170 GOQLFilter/whereexprrule.py:296 +msgid "Media matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:172 +msgid "" +"Matches media for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:190 GOQLFilter/whereexprrule.py:301 +msgid "Notes matching the " +msgstr "" + +#: GOQLFilter/whereexprrule.gpr.py:192 +msgid "" +"Matches notes for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + +#: GOQLFilter/whereexprrule.py:212 +msgid "GOQL expression" +msgstr "" + +#: GOQLFilter/whereexprrule.py:214 +msgid "" +"Matches objects for which the given gramps-object-query-language where-" +"expression evaluates to true" +msgstr "" + #: GedcomExtensions/GedcomExtensions.gpr.py:32 msgid "Export GEDCOM Extensions (GED2)" msgstr "" From 31dab23dcdd065ed83edb8842a47e3d3ec3ddbe0 Mon Sep 17 00:00:00 2001 From: Kaj Arne Mikkelsen Date: Wed, 5 Aug 2026 20:02:22 +0200 Subject: [PATCH 123/156] Translated using Weblate (Danish) Currently translated at 59.2% (3341 of 5640 strings) Translation: Gramps/Addons Translate-URL: https://hosted.weblate.org/projects/gramps-project/addons/da/ --- po/da.po | 229 +++++++++++++++++++++++-------------------------------- 1 file changed, 95 insertions(+), 134 deletions(-) diff --git a/po/da.po b/po/da.po index b295322f4..5655cc456 100644 --- a/po/da.po +++ b/po/da.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-04 08:55-0700\n" -"PO-Revision-Date: 2026-08-04 10:13+0000\n" +"PO-Revision-Date: 2026-08-05 18:01+0000\n" "Last-Translator: Kaj Arne Mikkelsen \n" "Language-Team: Danish \n" @@ -2256,10 +2256,8 @@ msgid "Clear all results and reset" msgstr "Fjern alle resultater og nulstil" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:158 -#, fuzzy -#| msgid "Internet type filter" msgid "Ready to search" -msgstr "Internet type filter" +msgstr "Klar til at søge" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:185 #, python-brace-format @@ -2267,6 +2265,8 @@ msgid "" "Search Depth: {depth} | People Processed: {processed} | Queue Size: " "{queue_size}" msgstr "" +"Søgedybde: {depth} | Personer behandlet: {processed} | Køstørrelse: " +"{queue_size}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:216 msgid "mentioned in note" @@ -2303,24 +2303,20 @@ msgstr "" " %s af " #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:336 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Home Person set" -msgstr "Ingen aktive person valgt." +msgstr "Fejl: Ingen proband valgt." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:341 msgid "No Active Person set." msgstr "Ingen aktive person valgt." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:342 -#, fuzzy -#| msgid "No Active Person set." msgid "Error: No Active Person set" -msgstr "Ingen aktive person valgt." +msgstr "Fejl: Ingen aktiv person valgt" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:347 msgid "Initializing search..." -msgstr "" +msgstr "Initialiserer søgning..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:366 msgid "Looking for relationship between\n" @@ -2337,10 +2333,8 @@ msgid " %s (Active Person)...\n" msgstr " %s (Active Person)...\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:382 -#, fuzzy -#| msgid "Fetching records..." msgid "Searching for connections..." -msgstr "Henter poster..." +msgstr "Leder efter forbindelser..." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:402 #, python-format @@ -2358,50 +2352,33 @@ msgstr "" "Tryk Fortsæt for at søge efter flere slægtskaber.\n" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:427 -#, fuzzy -#| msgid "" -#| "Paused.\n" -#| "Press Continue to search for additional relations.\n" msgid "Paused - Press Continue to search for more relations" -msgstr "" -"Pauset.\n" -"Tryk Fortsæt for at søge efter flere slægtskaber.\n" +msgstr "Pauset - Tryk Fortsæt for at søge efter flere slægtskaber" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:456 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." +#, python-format msgid "" "\n" "Search completed. %d relation paths found." msgstr "" "\n" -"Søgning afsluttet. %d slægtskaber fundet." +"Søgning afsluttet. %d slægtskabslinjer fundet." #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:460 -#, fuzzy -#| msgid "" -#| "\n" -#| "Search completed. %d relations found." msgid "Search completed - {} relation paths found" -msgstr "" -"\n" -"Søgning afsluttet. %d slægtskaber fundet." +msgstr "Søgning afsluttet - {} slægtskabslinjer fundet" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:471 msgid "Error during search: {}" -msgstr "" +msgstr "Fejl ved søgning: {}" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:481 -#, fuzzy -#| msgid "Removing '%s'..." msgid "Resuming search..." -msgstr "Fjerner \"%s\" ..." +msgstr "Genoptager søgning…" #: DeepConnectionsGramplet/DeepConnectionsGramplet.py:489 msgid "Search interrupted by user" -msgstr "" +msgstr "Søgning afbrudt af bruger" #: DenominoViso/DenominoViso.gpr.py:9 msgid "DenominoViso" @@ -2981,16 +2958,15 @@ msgid "Writing %s reports..." msgstr "Skriver %s rapporter..." #: DescendantBooks/DescendantBookReport.py:442 -#, fuzzy, python-brace-format -#| msgid "%(report_count)s. Descendant Report for %(person_name)s" +#, python-brace-format msgid "{report_count}. Descendants of {name}" -msgstr "%(report_count)s. efterkommerrapport for %(person_name)s" +msgstr "{report_count}. Efterkommere til {name}" #: DescendantBooks/DescendantBookReport.py:481 #: DescendantBooks/DetailedDescendantBookReport.py:619 #, python-brace-format msgid "{report_count:d}. {name}" -msgstr "" +msgstr "{report_count:d}. {name}" #: DescendantBooks/DescendantBookReport.py:510 #: DescendantBooks/DetailedDescendantBookReport.py:1371 @@ -3057,10 +3033,9 @@ msgid "Report, Generation, Person, Name" msgstr "Rapport, Generation, Person, Navn" #: DescendantBooks/DetailedDescendantBookReport.py:699 -#, fuzzy, python-brace-format -#| msgid "See Report : %s, Generation : %s, Person : %s" +#, python-brace-format msgid "See Report : {report}, Generation : {generation}, Person : {person}" -msgstr "See Report : %s, Generation : %s, Person : %s" +msgstr "Se Rapport : {report}, Generation : {generation}, Person : {person}" #: DescendantBooks/DetailedDescendantBookReport.py:767 #: DescendantBooks/DetailedDescendantBookReport.py:779 @@ -3069,16 +3044,15 @@ msgid "Report appearances for %s" msgstr "Rapport udseende for %s" #: DescendantBooks/DetailedDescendantBookReport.py:772 -#, fuzzy, python-brace-format -#| msgid "Spouse of: Report: %s, Generation: %s, Person: %s" +#, python-brace-format msgid "Spouse of: Report: {report}, Generation: {generation}, Person: {person}" -msgstr "Ægtefælle til: Rapport: %s, Generation: %s, Person: %s" +msgstr "" +"Ægtefælle til: Rapport: {report}, Generation: {generation}, Person: {person}" #: DescendantBooks/DetailedDescendantBookReport.py:784 -#, fuzzy, python-brace-format -#| msgid "Report: %s, Generation: %s, Person: %s" +#, python-brace-format msgid "Report: {report}, Generation: {generation}, Person: {person}" -msgstr "Rapport: %s, Generation: %s, Person: %s" +msgstr "Rapport: {report}, Generation: {generation}, Person: {person}" #: DescendantBooks/DetailedDescendantBookReport.py:836 #, python-format @@ -3129,7 +3103,7 @@ msgstr " %(event_text)s" #: DescendantBooks/DetailedDescendantBookReport.py:1023 #, python-brace-format msgid "Ref: {number}. {name}" -msgstr "" +msgstr "Ref: {number}. {name}" #: DescendantBooks/DetailedDescendantBookReport.py:1263 #: DetDescendantReport-images/detdescendantreporti.py:812 @@ -3257,10 +3231,8 @@ msgid "Gramplet for showing people and descendant counts" msgstr "Gramplet der viser personer og antal efterkommere" #: DescendantSpaceTree/DescendantSpaceTree.gpr.py:26 -#, fuzzy -#| msgid "Descendant Indented Tree" msgid "Descendant Space Tree" -msgstr "Indrykket træ for efterkommere" +msgstr "Space Tree for efterkommere" #: DescendantSpaceTree/DescendantSpaceTree.gpr.py:36 msgid "" @@ -3268,122 +3240,103 @@ msgid "" "a Space Tree for efficient viewing, even with many descendants or " "generations." msgstr "" +"Danner en webside med en interaktiv graf over efterkommere repræsenterede " +"som et Space Tree for effektiv visning, sselv med mange efterkommere eller " +"generationer." #: DescendantSpaceTree/DescendantSpaceTree.py:102 msgid "Patriarchal Line (Male ancestor)" -msgstr "" +msgstr "Patriarkal linje (mandlige ane)" #: DescendantSpaceTree/DescendantSpaceTree.py:103 msgid "Matriarchal Line (Female ancestor)" -msgstr "" +msgstr "Matriarkal linje (kvindelig ane)" #: DescendantSpaceTree/DescendantSpaceTree.py:106 -#, fuzzy -#| msgid "Person theme" msgid "Dark theme" -msgstr "Person tema" +msgstr "Mørkt tema" #: DescendantSpaceTree/DescendantSpaceTree.py:107 -#, fuzzy -#| msgid "Light" msgid "Light theme" -msgstr "Lyst" +msgstr "Lyst tema" #: DescendantSpaceTree/DescendantSpaceTree.py:426 -#, fuzzy -#| msgid "Life Line Descendant Chart" msgid "Continue Descendant Tree" -msgstr "Life Line Efterkommerdiagram" +msgstr "Fortsæt efterkommer træ" #: DescendantSpaceTree/DescendantSpaceTree.py:427 -#, fuzzy -#| msgid "Alternate name" msgid "Alternate Descendant Tree" -msgstr "Alternativt navn" +msgstr "Alternativt efterkommer træ" #: DescendantSpaceTree/DescendantSpaceTree.py:455 -#, fuzzy -#| msgid "Descendant Indented Tree" msgid "Descendant SpaceTree" -msgstr "Indrykket træ for efterkommere" +msgstr "SpaceTree for efterkommere" #: DescendantSpaceTree/DescendantSpaceTree.py:635 -#, fuzzy -#| msgid "Trim descendants" msgid "Total descendants" -msgstr "Trim efterkommere" +msgstr "Totalt antal efterkommere" #: DescendantSpaceTree/DescendantSpaceTree.py:659 -#, fuzzy -#| msgid "Marriages/Families" msgid "Marriages/Families: