Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advice - just use .po files? #1128

Open
robertlagrant opened this issue Sep 19, 2024 · 2 comments
Open

Advice - just use .po files? #1128

robertlagrant opened this issue Sep 19, 2024 · 2 comments

Comments

@robertlagrant
Copy link

robertlagrant commented Sep 19, 2024

For the project I'm working on - a simple Litestar-implemented API - we are using Babel to manage translation responses.

For us, .pot, .po and .mo files are overkill. We commit .po files to source control, but then on build/push of our code we generate .mo files so they're available to the application on install. We have commands to create .pot files, apply them as a diff to our .po files, and then instantly delete the .pot files.

Is there a way of working that just involves using .po files? I'd love to be able to commit .po files to my repo, have them included in the build, and then be read and cached on startup. It would also make running tests and other scenarios much simpler to just have one file type.

Has anyone done this? Could I just do read_po() into Catalog files that I cache and read from?

@robertlagrant
Copy link
Author

I actually bit the bullet and had a go at this myself.

class TranslatorFunction(Protocol):
    def __call__(self, message_id: str, locale: str) -> str:
        ...


def create_t(translations_path: Path) -> TranslatorFunction:
    """Returns a t function populated with all of the translations detected in the file system."""
    translations = {}

    for po_file_path in translations_path.glob("**/LC_MESSAGES/*.po"):
        po_file_relative_path = po_file_path.relative_to(translations_path)

        locale = po_file_relative_path.parts[0]
        domain = po_file_relative_path.parts[2].removesuffix(".po")

        po_file_contents = StringIO(Path(po_file_path).read_text())

        catalogue: Catalog = read_po(po_file_contents)
        catalogue.locale = locale
        catalogue.domain = domain

        translations[locale] = catalogue

    supported_locales = set(translations.keys())

    def gettext(message_id: str, locale: str) -> str:
        if locale not in supported_locales:
            raise ValueError(
                f'Unavailable locale "{locale}" requested. Available: {supported_locales}.'
            )

        return translations[locale].get(message_id).string

    return gettext


t = create_t(TRANSLATIONS_DIRECTORY)

@tomasr8
Copy link
Member

tomasr8 commented Sep 21, 2024

Nice! The only issue I see is the gettext implementation. It is possible that the message you are looking for is pluralized in which case you have to look at the plurals as well:
https://github.com/python/cpython/blob/342e654b8eda24c68da64cc21bc9583e480d9e8e/Lib/gettext.py#L438-L447
The problem is that the plural method is part of the GNUTranslations class which you want to avoid. You could probably do somehing like this instead:

from gettext import c2py

plural_fn = c2py(catalog._plural_expr)

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

No branches or pull requests

2 participants