Skip to content

Add NameSuite Addon (tool + gramplet) to fix, infer and audit given and patronymic names#941

Merged
GaryGriffin merged 13 commits into
gramps-project:maintenance/gramps60from
bryndin:maintenance/gramps60
Jul 3, 2026
Merged

Add NameSuite Addon (tool + gramplet) to fix, infer and audit given and patronymic names#941
GaryGriffin merged 13 commits into
gramps-project:maintenance/gramps60from
bryndin:maintenance/gramps60

Conversation

@bryndin

@bryndin bryndin commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

@GaryGriffin

Copy link
Copy Markdown
Member

Thanks for the submission. Couple comments

When I tried to run, I got:
Plugin error (from 'names_tool'): No module named 'name_processor' and
Plugin error (from 'patronymics_gramplet'): No module named 'name_processor'

Running unittests:

python3 -m unittest NameSuite.tests.controllers.test_tool
E
======================================================================
ERROR: test_tool (unittest.loader._FailedTest.test_tool)
----------------------------------------------------------------------
ImportError: Failed to import test module: test_tool
Traceback (most recent call last):
  File "/GrampsTest/gtk/inst/lib/python3.13/unittest/loader.py", line 137, in loadTestsFromName
    module = __import__(module_name)
  File "/Development/addons-source/NameSuite/tests/controllers/test_tool.py", line 2, in <module>
    from name_processor.controllers.tool import ToolController
ModuleNotFoundError: No module named 'name_processor'


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

@bryndin

bryndin commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback.
It looks like the import structure must be based on the NameSuite. Somehow, I got an impression from the docs that addons themselves are brought into the namespace, and it did work with local Gramps.
Let me look at it.

@GaryGriffin

Copy link
Copy Markdown
Member

I ran the unittest from the addons-source directory.

By default (without a MANIFEST) only Package your Addon files in the Addon directory are put into the download file.

@bryndin

bryndin commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@GaryGriffin

A few questions:

  • I use pytest for fixtures and running tests, can it be used, or should I convert tests to the vanilla unittest? (reuirements.txt inside NameSuite are for the tests + linting only).
  • Imports must be addressed via the package name from NameSuite.name_processor ..., right? I'll add them.
  • Do I have to add the license block to every Python file (including tests?), or adding a package-wide LICENSE.txt or something would work?
  • Should I remove the existing README.md and screenshots? I presume I'll be able to create a wiki page later?
  • Should I add __init__.py to the addon root dir (NameSuite)?

@GaryGriffin

Copy link
Copy Markdown
Member

A few questions:

I use pytest for fixtures and running tests, can it be used, or should I convert tests to the vanilla unittest? (reuirements.txt inside NameSuite are for the tests + linting only).

You should use unittest instead of pytest for the Addons repo.

Imports must be addressed via the package name from NameSuite.name_processor ..., right? I'll add them.

imports are relative to the test file location.

Do I have to add the license block to every Python file (including tests?), or adding a package-wide LICENSE.txt or something would work?

Every python file should have the license info in the header. Be sure to include your copyright info.

Should I remove the existing README.md and screenshots? I presume I'll be able to create a wiki page later?

If you do not want them in the download, then they are not needed in Github. They can remain if you want - your call. They could be the basis for the help_url.

Should I add init.py to the addon root dir (NameSuite)?

It will be needed in every directory that has a test. If you flatten and only have a single tests directory, then there. If you keep the multi-level directory structure, you will need in each of the sub directorie that have tests.

@bryndin

bryndin commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@GaryGriffin updated the PR

  1. Tests use unittest exclusively.
    Running tests from /addons-source works:
python3 -m unittest NameSuite.tests.controllers.test_tool
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK
python3 -m unittest discover -s NameSuite
....................................................
----------------------------------------------------------------------
Ran 52 tests in 0.020s
  1. Added license headers to all Python files.
  2. Remove unneeded files (e.g. README, screenshots, etc.)
  3. Added MANIFEST file for the txt files used in tests
  4. Dropped NameSuite. prefix from abs imports

Please use a real email in the GPR file.

My email in GPR is real, it's the GitHub-specific email GitHub creates for devs.

@GaryGriffin

Copy link
Copy Markdown
Member

All tests worked. Tried Addon and got warnings (errors):

Audit Given and Patronymic Names

WARNING: _manager.py: line 328: Plugin error (from 'names_tool'): No module named 'name_processor'

Patronymic Suggestion

WARNING: _manager.py: line 328: Plugin error (from 'patronymics_gramplet'): No module named 'name_processor'

You need to include the name_processor files in the MANIFEST. Currently only the python files in the NameSuite directory are in the download, you need to add the following to the MANIFEST

NameSuite/name_processor/*/*

After adding that, the Audit works. The Suggestion fails with:

Traceback (most recent call last):
File "/Applications/Gramps6.0.6.app/Contents/Resources/lib/python3.13/site-packages/gramps/gen/plug/_manager.py", line 270, in load_plugin
_module = self.import_plugin(pdata)
File "/Applications/Gramps6.0.6.app/Contents/Resources/lib/python3.13/site-packages/gramps/gen/plug/manager.py", line 304, in import_plugin
module = import(pdata.mod_name)
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/patronymics_gramplet.py", line 32, in
from name_processor.views.gramplet import GrampletView
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/name_processor/views/gramplet.py", line 42, in
class GrampletView:
...<80 lines>...
ErrorDialog(
(title_key), message, self.gramplet.gui.get_window())
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/name_processor/views/gramplet.py", line 76, in GrampletView
def set_controller(self, controller: GrampletController | None) -> None:
^^^^^^^^^^^^^^^^^^
NameError: name 'GrampletController' is not defined
2026-06-10 18:01:47.433: WARNING: grampletpane.py: line 191: Error loading gramplet 'patronymic_suggestion_gramplet': skipping content

Comments:

  • Since the __init__.py does not include code, it doesnt need the License. This file is only needed in the directories where there is a test file. The other directories do not need it.
  • tests/conftest.py is not needed
  • Is male_names.txt needed by user (included in MANIFEST?

@GaryGriffin

Copy link
Copy Markdown
Member

In NameSuite/name_processor/views/gramplet.py you conditionally import GrampletController, but in procedure __init__ you always use it.

Always importing it fixes the issue and the Addon works.

@GaryGriffin

Copy link
Copy Markdown
Member

Testing Audit

  • The wiki page is currently blank, so I dont have guidance on how to store Patronymic.
  • I am getting unexpected results for the few relevant names I have. I suspect where I am storing the Patronymic is different from your expectation. I am storing in the Given Name at the end. Where should the Patronymic be stored for your Addon? Are you using the Name Editor --> Origin field == Patronymic, for instance?

Father:
Screenshot 2026-06-11 at 8 54 41 AM

Audit:

Screenshot 2026-06-11 at 8 55 32 AM

@GaryGriffin

Copy link
Copy Markdown
Member

Changed my naming convention to put Patronymic in Surname (Origin=Patronymic) instead of Given Name. Audit Gramplet worked great.

@bryndin

bryndin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

The wiki page is currently blank, so I dont have guidance on how to store Patronymic.

Changed my naming convention to put Patronymic in Surname (Origin=Patronymic) instead of Given Name. Audit Gramplet worked great.

Confusingly, Gramps considers Patronymic names (e.g. Petrovich in Ivan Pertovich) a type of surname. For East Slavs it's not really a surname, but this historical hack works pretty well to the point Gramps documents it in the wiki. They later built a dedicated name format to visualize them properly and the hack became a feature.
image

Typing pat names manually and adjusting the surname type is time-consuming and far from fun. That's why I've built the gramplet.

For the large DBs (I have 2k+), manually copy-pasting pat names creates a lot of typos: names with the wrong gender, grandfather's names being accidentally used instead of the father's... It also helps to catch children attached to the wrong parents, thanks to pat names serving as a second check.

Is male_names.txt needed by user (included in MANIFEST?

It's a pat names corpus for tests. I have a quite advanced morphology engine to derive the correct endings for pat names. It requires testing on a corpus of real historical East Slavic names, as, to my knowledge, no complete set of linguistic rules exists, and some names are the official exceptions.
If we want to remove that file, we'd remove the entire test suite.

Since the init.py does not include code, it doesnt need the License.

That is easy to change. I have a script that adds licenses to files.

tests/conftest.py is not needed

Yes, good catch. That's pytest leftover.

The wiki page is currently blank

I thought the Addon must be live first?

In NameSuite/name_processor/views/gramplet.py you conditionally import GrampletController, but in procedure init you always use it.

GrampletController there is purely for typechecking. The controller is passed to the view, but Python's duck-typing doesn't need it to be declared or imported.

You need to include the name_processor files in the MANIFEST. Currently only the python files in the NameSuite directory are in the download, you need to add the following to the MANIFEST

I see. This part is quite hard to figure out. The other addons I've checked consist of just one file, so they aren't a good example. When I test by copying Python files to my Gramps plugin dir, everything works fine with no warnings or errors.
I'll add sub-dirs to the MANIFEST, thanks.

@bryndin

bryndin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@GaryGriffin I think I figured it out. Updated the repo.
Pls let me know if something else is off. I'm developing on Linux, while testing on Windows Gramps by copying files to $HOME/AppData/Roaming/gramps/${GRAPMPS_VER}/plugins/NameSuite

Here's the addon PR: gramps-project/addons#15

@GaryGriffin

Copy link
Copy Markdown
Member

Thanks for the better description of Patronymic. Some answers:

  • male_names.txt - I only meant to ask if this is needed by the user (that is, should it be in the MANIFEST and therefore in the distribution). It certainly is needed for tests. The tests are not part of the distribution to the user. After successfully using your new Addon, I agree that having the male_names.txt in the distribution (in the MANIFEST) is useful.
  • You can add the wiki help independently of the release. I prefer to have it when the initial PR is made so that any testers can see how to use a new Addon.
  • addons repo - There are a number of other things that need done when publishing an Addon (mostly related to string translations), so you dont need to do a PR to the addons repo - only the addons-source repo. When I publish, there is a series of make.py commands used. Generally, I wait a week from PR creation to allow for user testing before merge/publish of a new Addon. You can close PR15.

Open Issues:
Still an issue with GrampletController. Error is:

Gramps6.0.6.app/Contents/MacOS/Gramps
Traceback (most recent call last):
File "/Applications/Gramps6.0.6.app/Contents/Resources/lib/python3.13/site-packages/gramps/gen/plug/_manager.py", line 270, in load_plugin
_module = self.import_plugin(pdata)
File "/Applications/Gramps6.0.6.app/Contents/Resources/lib/python3.13/site-packages/gramps/gen/plug/manager.py", line 304, in import_plugin
module = import(pdata.mod_name)
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/patronymics_gramplet.py", line 32, in
from name_processor.views.gramplet import GrampletView
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/name_processor/views/gramplet.py", line 42, in
class GrampletView:
...<80 lines>...
ErrorDialog(
(title_key), message, self.gramplet.gui.get_window())
File "/Users/gary/Library/Application Support/gramps/gramps60/plugins/NameSuite/name_processor/views/gramplet.py", line 76, in GrampletView
def set_controller(self, controller: GrampletController | None) -> None:
^^^^^^^^^^^^^^^^^^
NameError: name 'GrampletController' is not defined

Specifically, views/gramplet.py :
line 36:37

if TYPE_CHECKING:
    from name_processor.controllers.gramplet import GrampletController

line 71:

    self._controller: GrampletController | None = None

This causes the Patronymic Suggestion Addon to fail. When I deleted line 36 and adjusted spacing on line 37, it worked.

Cleanup
You can remove the following files as they are not needed. Only directories with tests need this (for unittest discovery)

name_processor/__init__.py
name_processor/repositories/__init__.py
name_processor/models/__init__.py
name_processor/controllers/__init__.py
name_processor/views/__init__.py
name_processor/services/__init__.py
name_processor/services/audit_rules/__init__.py
tests/__init__.py

@bryndin

bryndin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Still an issue with GrampletController. Error is:

Thanks for the details, as I wasn't able to reproduce it with Windows Gramps 6.0.8.
Looks like Win and Mac versions are built with different Python versions, and the issue is related to PEP 649 introduced in Python 3.14, where they changed the format for type hints.

Let me figure out the most compatible solution.

Do you know the Python version used to build your Gramps? Don't worry if you don't, it's not a problem.

@GaryGriffin

Copy link
Copy Markdown
Member

Do you know the Python version used to build your Gramps? Don't worry if you don't, it's not a problem.

Mac version of Gramps 6.0.6 - 3.13
Mac version of Gramps 6.1 (beta1) - 3.14

Unless you disagree, my intention is to cherry pick this addon (once published on 6.0) to 6.1
My testing has been exclusively on Gramps 6.0.6, so it is running 3.13 .

@bryndin

bryndin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Unless you disagree, my intention is to cherry pick this addon (once published on 6.0) to 6.1 My testing has been exclusively on Gramps 6.0.6, so it is running 3.13 .

My Win Gramps uses 3.14.4, so that's why I wasn't able to see it. Good catch!

I'm going to update the code to make it compatible with all 3.10+. Bear with me.

@GaryGriffin

Copy link
Copy Markdown
Member

As another point of reference, Mac Gramps 5.1 and 5.2 use python 3.11 .

@GaryGriffin

Copy link
Copy Markdown
Member

A new guidelines wiki page for addon developers is being written. It is still very preliminary. Your comments would be appreciated.

@GaryGriffin

Copy link
Copy Markdown
Member

One suggestion. In the Audit Patronymic --> Audit Settings, it would be useful to add a Person Filter option. This would limit the part of the database that is searched. I have over 10K+ people but < 100 are Slavic names.

In my case, one person married someone from Russia and limiting the database search to only their ancestors and relations would reduce the scope of the search.

@bryndin

bryndin commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

One suggestion. In the Audit Patronymic --> Audit Settings, it would be useful to add a Person Filter option. This would limit the part of the database that is searched. I have over 10K+ people but < 100 are Slavic names.

In my case, one person married someone from Russia and limiting the database search to only their ancestors and relations would reduce the scope of the search.

Since I've gotten back to the codebase, I'm doing another pass, refactoring to make it better decoupled. This way, I'll be able to have normal E2E tests with minimal impact from Gramps imports (GTK, DB).
It will take me a few days.

I wish Gramps were more testable. Not being able to run headless Gramps (no GTK) to test DB access with fixtures is a major downside. Not having type annotation in Gramps is another safety downside. Seems like a low-hanging fruit, given the AI automation.

@bryndin

bryndin commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

A new guidelines wiki page for addon developers is being written. It is still very preliminary. Your comments would be appreciated.

I need to read the new one, but the comments on the old one:

P0: Lack of pointers to good examples. 90% of developers don't have time to read extensive docs. They would take and modify someone else's existing code. It was already like that pre-AI, now that ship had sailed. If developers find a bad/outdated example, it can spread bad practices across the codebase.

P1: Some data was outdated. AFAIR on "addons" repository, and on "requesting write access" to the Gramps repo, instead of forking it.

P1: The part on how addons are imported and what namespace is used kept me puzzled. All the other addons were simple one-file Python scripts that weren't representative.

P2: The MANIFEST-related part was confusing, and how the addon-source relates to addon repo.

P2: Moreover, I'd fine-tune the docs to be 50/50 Human/LLM parsable, provide clear must not guidelines that could be added to the AGENTS.md section.

P2: Since the guidelines mentioned above are... guidelines, and not enforcement. I'd develop tests, lints, etc., that would actually enforce validation on the addon-source repo.

These are a few from the top of my head. I'll check the updated docs, but it may take me some time. Need to balance my addon refactoring with other things.

@bryndin

bryndin commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

One suggestion. In the Audit Patronymic --> Audit Settings, it would be useful to add a Person Filter option. This would limit the part of the database that is searched. I have over 10K+ people but < 100 are Slavic names.

I may need your feedback on the speed of the audit. How long does it take on your DB?
Mine is 2K, and it takes about 13 secs.

I'm considering adding a caching layer to speed it up.
Optionally, I can drop the confidence guessing, but that will be a downgrade.

@GaryGriffin

Copy link
Copy Markdown
Member

I may need your feedback on the speed of the audit. How long does it take on your DB?

I have 17,500 people and it took 1 min 5 sec on an M3 Mac.

Not being able to run headless Gramps (no GTK) to test DB access with fixtures is a major downside.

I believe this is possible. Look at some of the recent changes that have unittests. SQLiteWithSelect may be a good example of test cases.

I need to read the new one, but the comments on the old one:

P0: Lack of pointers to good examples. 90% of developers don't have time to read extensive docs. They would take and modify someone else's existing code. It was already like that pre-AI, now that ship had sailed. If developers find a bad/outdated example, it can spread bad practices across the codebase.

I wrote a Sample addon a few years ago but it was more about using the API and not about guidelines. You bring up a good point.

P1: Some data was outdated. AFAIR on "addons" repository, and on "requesting write access" to the Gramps repo, instead of forking it.

I updated the docs to clarify.

P1: The part on how addons are imported and what namespace is used kept me puzzled. All the other addons were simple one-file Python scripts that weren't representative.

Yours is one of the first that has subdirectories with files where this is an issue.

P2: The MANIFEST-related part was confusing, and how the addon-source relates to addon repo.

Does Package your addon make this clear, or is this where the confusion lies?

P2: Moreover, I'd fine-tune the docs to be 50/50 Human/LLM parsable, provide clear must not guidelines that could be added to the AGENTS.md section.

P2: Since the guidelines mentioned above are... guidelines, and not enforcement. I'd develop tests, lints, etc., that would actually enforce validation on the addon-source repo.

We are moving to a CI/CD process for the addons-source repo, so guidelines are likely to be more rigorous. Lint and black are still suggestions. But there are testing processes for CI/CD that we are now running (not in production yet).

@bryndin

bryndin commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@eduralph let me know where to post the feedback to your GERP, and we can move it there.


TL;DR: IMHO, what you're describing is the Gramps Public API. Taking that perspective may straighten the whole GERP.

Part 1: The Addon API surface
The Addon API is the set of names, signatures, and behaviours that Gramps core promises to keep stable for addon authors. It is not the entire Gramps codebase; an addon poking at gramps.gen.db.generic._Generic.some_private has always been on its own.

Access to private internals ends up being the core maintainer's problem on every migration. I'd restrict it. If some functionality is missing, it'd be added to the API.
Full decoupling could be hard for the non-isolated in-process Python addons, and may not be worth the effort and added complexity.

Versioning scheme

This all makes sense. Semver is standard in Python packages. This section could be slimmed down. If I'm not missing any internal details, the message is

  • Semver 2.0; API version is decoupled from the Gramps version.

Detecting API changes
Tier 1 — Define the surface explicitly.

If you ask me, I'd approach this one differently. Python has Protocols that were designed specifically to declare APIs/Interfaces. ABCs are the historical option, but Protocols are superior. They provide static typing (that Gramps is very past due on), and allow Addons to be isolated from the Gramps internals.

Having a whitelist of imports is
- P0: Provides poor isolation (Python doesn't enforce private scope). You allow an import, you leak the internals.
- P0: Very confusing for AI coders
- P1: Confusing to human developers
- P2: Harder to enforce

Pros: Gramps already has a DB API that can and should be converted to Protocols anyway.
Cons: That API can grow pretty darn big.

Tier 2 — Mechanical detection via griffe

Great point, I'd even extend this tooling section. With the modern speed of development, the guardrails are a must.

Part 2: Lifecycle (two orthogonal axes)

Don't have a background, not going to comment on it. These things are highly opinionated and tend to consume time in discussions. I prefer not to go there unless there is a clear pain point everyone agrees on.

MAINTAINERS wiki integration

Oh my, pls replace Wiki with the GitHub code-based (or some automated) solution. I know it's out of scope for this GERP, just throwing it here.

Part 3: Source repository structure

Hide the entire concept of addon repo from users. It must be an internal detail of the Gramps build system. The packaging must be testable using the addon-source providing tools only.

Open questions
Whether the dual-version scheme (Gramps release version + addon_api)

For web services, libraries, etc., the Public API defines the main (and only) version, as that's what customers face. For Gramps as a desktop app, it could be different. Users may see a major change in functionality without a change in the API. I'm less of an expert there, but the independent Gramps & API versioning makes sense.

Default maintenance_state for legacy addons during Phase 3 rollout.
BROKEN visibility.

If agreement is slow to reach, I'd kick it into a separate low-priority proposal.

Cross-major-version backport workflow.

Do what Python does: support old versions :-)

Developer tooling around registration

I'd raise priority on this one heavily. Registration is a pain point for addon developers. It's a Python file being non-Python. No tests, linting, or type hinting. If I, as an addon dev, mess it up, I can't test it within the addon-source repo.

Hope it helps

@GaryGriffin

Copy link
Copy Markdown
Member
  1. Update for translation fix
  2. Generate po/template.pot
  1. I will replace my current core binding:
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext

With this

from gramps.gen.const import GRAMPS_LOCALE as glocale

try:
    _trans = glocale.get_addon_translator(__file__)
except ValueError:
    _trans = glocale.translation
_ = _trans.gettext

@eduralph Is this complete for the CI/CD process? The template.pot seems reasonable and I am merging your make.py changes so that point is doubly covered.

@eduralph

Copy link
Copy Markdown
Contributor

@bryndin — small but important: get_addon_translator(file) loads the catalog from dirname(file)/locale, so binding _ in the name_processor/views/* modules makes it look under name_processor/views/locale, which doesn't exist — the .mo files live at NameSuite/locale/. I tested it against real Gramps with a compiled ru/LC_MESSAGES/addon.mo: bound in a nested module, a Russian user gets the untranslated English string (the ValueError falls through to the core catalog); anchored at the addon root it returns the translation. So the idiom needs to be anchored at the addon root, not each module's file.

Cleanest: bind _ once in a new name_processor/i18n.py:

import os
from gramps.gen.const import GRAMPS_LOCALE as glocale

# this file is /name_processor/i18n.py → the addon root (which holds locale/)
# is two dirs up; get_addon_translator uses only dirname(path), so "_" is a placeholder.
_ADDON_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(file)))
try:
_trans = glocale.get_addon_translator(os.path.join(ADDON_ROOT, ""))
except ValueError:
_trans = glocale.translation
_ = _trans.gettext
then in each of the five view modules, replace the glocale import and the try/except _trans block with:

from name_processor.i18n import _
I verified this loads NameSuite/locale/.../addon.mo correctly ("Given name" → "Имя").

@bryndin

bryndin commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@eduralph
I see a few problems there:

  • It's a workaround for this particular addon (and the model layout)
  • It's not reusable, and harder to maintain
  • Addon devs have to understand how Gramps translation works (while they're better off improving their addons)

While I can do it to unblock this PR, the better options could be:

Option 1: Make Gramps get_addon_translator() handle it.

Say, add the logic to do locale/ dir discovery.
Pros:

  • Easy to document
  • Simplest solution for addon devs, they can blindly reuse existing code samples
  • Takes effort off the addon-source repo maintainers, simplifies their CRs
  • Easy to make changes without migrating all the addons

Cons:

  • It's a Gramps level change

Option 2: Make each model a module (NameSuite will be the module)

Translation logic is a copy-paste i18n.py file, placed at the root of the addon module. Implement auto-discovery there.
Pros:

  • Simple copy-paste of the reusable file

Cons:

  • Still IMHO a redundant step.

Separate question to both @GaryGriffin and @eduralph

Would it be better to make Addons the real Python modules?
from NameSuite.name_processor.models.infer import ...
instead of
from name_processor.models...

IMHO, this would streamline the development and make everyone's life much simpler.
I have quite some experience with Python, and I find the current system highly counterintuitive and puzzling. The struggle with translations is yet another indicator. We create workarounds for problems that may not need to exist. If needed, the Addon manager could be a simple wrapper around pip or a similar standard tool.

Modules must be self-contained, providing and handling their own tests, test frameworks, and whatnot. It's not the addon-source responsibility to deal with any of that, maybe besides test self-discovery.

This'd make life way easier for both addon devs and Gramps devs/maintainers.

@dsblank

dsblank commented Jun 27, 2026

Copy link
Copy Markdown
Member

I have quite some experience with Python, and I find the current system highly counterintuitive and puzzling. The struggle with translations is yet another indicator. We create workarounds for problems that may not need to exist. If needed, the Addon manager could be a simple wrapper around pip or a similar standard tool.

I was thinking the same thing. We have the foundation of our own mini-installer now; we could use that infrastructure... or something even easier.

@eduralph

Copy link
Copy Markdown
Contributor

@bryndin

I'd raise priority on this one heavily. Registration is a pain point for addon developers. It's a Python file being non-Python. No tests, linting, or type hinting. If I, as an addon dev, mess it up, I can't test it within the addon-source repo.

Hope it helps

Thanks for your ideas, I think there is some really good ideas to include in the GEPS - I'll revise it.

@Nick-Hall

Copy link
Copy Markdown
Member

@GaryGriffin If you are thinking about changing the way addon translations are handled, then you should also consider the possibility of compiling and distributing the aggregated translations instead of extracting them. This would have the benefit of separating the translation files from the individual addons. Users wouldn't see a new addon version for just a translation update.

@GaryGriffin

Copy link
Copy Markdown
Member

Should the translation topic move to a separate discussion rather than this PR? It is above my Python knowledge to suggest a path forward. I think the appropriate people ( @bryndin @eduralph @dsblank at a minimum ) need to drive to a plan for how to handle addon translations, as they seem already involved with it.

@dsblank

dsblank commented Jun 27, 2026

Copy link
Copy Markdown
Member

The translation question should be made in "discussions" in the gramps repo.

@eduralph

Copy link
Copy Markdown
Contributor

@bryndin - your Option 2 is the thing I proposed — both bind _ once, anchored at the addon root (the dir that holds locale/), and both let the view modules just from … import _. The reason it can't be a per-module file binding isn't a stylistic choice on my end: get_addon_translator(filename) derives the catalog dir as os.path.dirname(os.path.abspath(filename)) + "/locale" (gen/utils/grampslocale.py). So a binding in name_processor/views/base_tab.py looks under name_processor/views/locale/, which doesn't exist, and a Russian user silently gets English. Anchoring at the addon root is what makes it resolve NameSuite/locale/…/addon.mo.

@bryndin

bryndin commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

@GaryGriffin @eduralph @dsblank @Nick-Hall

I jammed on the options for the i18n design, here's a high-level RFC (it's human-readable):

  • P0: reduce the burden on addon developers
  • P1: decouple translation updates from code releases

If you are time restricted, check the best option:

@Nick-Hall,

  • Is self-hosting Weblate an option? Optionally, Weblate might be willing to host it for free for a popular open-source project.
  • Would Core Gramps translation benefit from the same (lang-pack) approach? (I'm not natively familiar with Gramps internals)

We can move this discussion elsewhere.
Note: Wiki registration is broken. I can't register there. No write access.

@GaryGriffin

Copy link
Copy Markdown
Member

Your Translation Workflow for Addons was correct up to v5.2 . However, version 6.0 translations moved to a Weblate process (Gramps core moved to a Weblate process during 5.x), found at https://hosted.weblate.org/projects/gramps-project/addons/ . Both Gramps core and Addons have separate Weblate projects for translations. The current translation process for Addons is:

  • Each addon has a po/template.pot. I run make.py .. init to create if it does not exist.
  • If a PR contains a change in a text string, I run make.py .. aggregate-pot to generate an updated consolidated strings file for Weblate. This file is at addons-source/po/addons.pot. Any string that is in the Gramps project string list (found at https://github.com/gramps-project/gramps/blob/maintenance/gramps60/po/gramps.pot ) is removed as a duplicate.
  • Translators make changes in Weblate for the Addons project (there is a separate project for Gramps). Each change appends to a single Addons PR generated by Weblate. The current PR is Translations update from Hosted Weblate #938 .
  • Every few weeks, I merge this PR. It updates the addons-source/po/*.po files (one per language). I also run make.py .. extract-po that updates every addon's po files stored in addons-source/.../po/*-local.po
  • When a new download is created, it includes the latest po/*-local.po files.

@bryndin

bryndin commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @GaryGriffin , I'll update the RFC. The "best solution" should stay the same, but the details need a rewrite.

@bryndin

bryndin commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@eduralph updated the i18n logic, it resides in the dedicated NameSuite/name_processor/views/i18n.py

@bryndin

bryndin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

"Gramps Addons - Current l10n process"
https://gist.github.com/bryndin/d95b239902c66f6072eb86732f89810a

This is the summary of the current process, as far as I understand it. @GaryGriffin is the process correct? Let me know if you have better documentation.

@bryndin

bryndin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Here's the updated RFC: https://gist.github.com/bryndin/b0fbd268b4785d0e6600b8270daca75e
The top part is the generalized architecture, followed by the concrete implementation in Gramps.

Description of the current L10n process in Gramps:
https://gist.github.com/bryndin/d95b239902c66f6072eb86732f89810a

@GaryGriffin

Copy link
Copy Markdown
Member

Suggest you start a discussion at https://github.com/gramps-project/gramps/discussions as this will require gramps core changes. This also gives a wider discussion area.

@GaryGriffin

Copy link
Copy Markdown
Member

@eduralph does the PR align with the CI proposed process and translations? From your perspective, it is ready to merge?

@eduralph

eduralph commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
  • Structure + template.pot: aligned.
  • CI/make.py flow: the make.py changes he's merging cover the aggregate-pot/Weblate side; structurally the addon (.gpr.py, MANIFEST, po/template.pot, tests/) conforms.
  • Translations: not ready to merge as-is — the runtime binding resolves the wrong locale/ dir, so translations silently won't apply. Needs the one-line i18n.py fix, after which it's aligned.

_trans` = glocale.get_addon_translator(os.path.join(_ADDON_ROOT, "name_processor.gpr.py"))

would do.

@bryndin

bryndin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author
  • Structure + template.pot: aligned.
  • CI/make.py flow: the make.py changes he's merging cover the aggregate-pot/Weblate side; structurally the addon (.gpr.py, MANIFEST, po/template.pot, tests/) conforms.
  • Translations: not ready to merge as-is — the runtime binding resolves the wrong locale/ dir, so translations silently won't apply. Needs the one-line i18n.py fix, after which it's aligned.

_trans` = glocale.get_addon_translator(os.path.join(_ADDON_ROOT, "name_processor.gpr.py"))

would do.

@eduralph should there be a repository-wide test for that, so that contributors like me can self-test it?

@eduralph

eduralph commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
  • Structure + template.pot: aligned.
  • CI/make.py flow: the make.py changes he's merging cover the aggregate-pot/Weblate side; structurally the addon (.gpr.py, MANIFEST, po/template.pot, tests/) conforms.
  • Translations: not ready to merge as-is — the runtime binding resolves the wrong locale/ dir, so translations silently won't apply. Needs the one-line i18n.py fix, after which it's aligned.

_trans` = glocale.get_addon_translator(os.path.join(_ADDON_ROOT, "name_processor.gpr.py"))
would do.

@eduralph should there be a repository-wide test for that, so that contributors like me can self-test it?

No, unfortunately not but is a good idea for a rule in a precommit hook. Definitely something to add to it once we get a CI process in place.

@bryndin

bryndin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

No, unfortunately not but is a good idea for a rule in a precommit hook. Definitely something to add to it once we get a CI process in place.

My understanding, a direct implementation would require all the Gramps dependencies, which is a no-go for the addon contributors.
I wonder if that could be achieved via some tricky mocking, but I'm not an expert and don't have an answer.

@bryndin

bryndin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

_trans` = glocale.get_addon_translator(os.path.join(_ADDON_ROOT, "name_processor.gpr.py"))

@eduralph updated, can you pls check?

@eduralph

eduralph commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@bryndin - all green, no more nit-picks

@GaryGriffin
GaryGriffin merged commit cb769d6 into gramps-project:maintenance/gramps60 Jul 3, 2026
@bryndin

bryndin commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

I've posted the RFC: Redesigning the L10n Architecture for Gramps Addons
gramps-project/gramps#2462

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants