Metadata-Version: 2.5
Name: vue-i18n-python
Version: 0.2.0
Summary: An unofficial Python port of vue-i18n: same JSON message files, same API, same behaviour.
Project-URL: Homepage, https://github.com/velis74/vue-i18n-python
Project-URL: Repository, https://github.com/velis74/vue-i18n-python
Project-URL: Issues, https://github.com/velis74/vue-i18n-python/issues
Project-URL: Documentation, https://docs.velis.si/vue-i18n-python/
Author-email: Jure Erznožnik <jure.erznoznik@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cldr,i18n,l10n,plural,translation,vue-i18n
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Internationalization
Classifier: Topic :: Software Development :: Localization
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: babel; extra == 'dev'
Requires-Dist: build; extra == 'dev'
Requires-Dist: coverage; extra == 'dev'
Requires-Dist: hatchling; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: ruff>=0.16; extra == 'dev'
Provides-Extra: intl
Requires-Dist: babel>=2.14; extra == 'intl'
Description-Content-Type: text/markdown

# vue-i18n-python

**An unofficial Python port of [vue-i18n](https://github.com/intlify/vue-i18n).** Not affiliated
with or endorsed by intlify; vue-i18n is © kazuya kawaguchi and contributors, MIT licensed.

Same JSON message files, same API, same behaviour — in Python. The point is a Vue front end and a
Python back end reading one set of translations and producing the same strings from them.

```python
import json

from vue_i18n import create_core_context, translate

ctx = create_core_context(
    locale="sl",
    fallback_locale="en",
    messages={                                     # the files your front end already loads
        "sl": json.load(open("locales/sl.json")),
        "en": json.load(open("locales/en.json")),
    },
)

translate(ctx, "hello", {"name": "Jure"})          # 'Zdravo Jure'
translate(ctx, "trips", 5)                         # plural form for 5
```

**[docs.velis.si/vue-i18n-python](https://docs.velis.si/vue-i18n-python/) is the manual.**

| | |
|---|---|
| Why this exists, and who should not use it | [Rationale](https://docs.velis.si/vue-i18n-python/guide/rationale.html) |
| Installing it and the call shapes | [Getting started](https://docs.velis.si/vue-i18n-python/guide/getting-started.html) |
| The one thing that surprises people | [Plural rules](https://docs.velis.si/vue-i18n-python/guide/plurals.html) |
| Where it deliberately differs from vue-i18n | [Differences](https://docs.velis.si/vue-i18n-python/guide/differences.html) |
| Every public function | [API reference](https://docs.velis.si/vue-i18n-python/api/) |

Message files are ordinary vue-i18n resources: `{named}` placeholders, `{0}` positional values,
`a | b | c` plurals, `@:linked` messages, `@.upper:` modifiers, `{'@'}` literals. Nothing asks you
to migrate a file or learn a syntax, and `vue-i18n-extract` and the rest of the toolchain keep
working.

Option names are vue-i18n's, in snake_case: `fallbackLocale` is `fallback_locale`, `missingWarn` is
`missing_warn`.

## Checking what you wrote

`t("cart.emty")` does not fail — it renders the key and waits for a customer to find it. The
bundled checker finds it first, on both ends of the project:

```sh
python -m vue_i18n.check locales/ --source backend/ --source frontend/src
```

Broken `@:links`, messages that will not compile, keys one locale is missing, keys the code asks for
and no locale has. Python is parsed; `.vue` and `.ts` are pattern-matched, which is approximate and
labelled so — and still more than `xgettext`, which cannot open a `.vue` file at all.

## How faithful is it?

Faithful enough that the claim is checked rather than asserted. Every part of the port is recorded
against the real thing and replayed on every test run:

| corpus | what it pins |
|---|---|
| `conformance/translate.json` | 57 `translate()` calls against `@intlify/core-base` itself |
| `conformance/render.json` | 74 messages rendered by real `vue-i18n` |
| `conformance/compile.json` | 128 messages: ASTs, error codes, error messages, source locations |
| `conformance/resolve.json` | 200 key-path resolutions |
| `conformance/fallback.json` | 374 fallback chains |
| `conformance/display.json` | 1612 values through JavaScript's `String()` semantics |

The recordings are generated by running upstream, never written by hand —
`scripts/conformance/README.md` says how to regenerate them.

## Following upstream

This port tracks vue-i18n's **released** versions, never a pre-release. Upstream is a git submodule
pinned to the version being tracked, so "which vue-i18n does this match" is answered by a checkout
rather than by a claim. `UPSTREAM.md` records the pin, maps every module to the upstream file it
came from, and holds the procedure for moving to a new release.

## Where it differs

Some differences are forced by the platform: JavaScript strings are UTF-16 and Python's are not,
`Intl` does not exist here so `d()` and `n()` go through `babel` with different CLDR data, and a
Python `int` stays exact past 2^53.

Others are chosen, and each one is written down in `PROGRESS.md` with the reasoning, a comment at
the site and a test that asserts it. `GAPS.md` lists what is knowingly missing.

## Plural rules

**If your Vue application already passes `pluralRules` to `createI18n`, pass the same rule here and
nothing changes.** A rule is `(choice, choices_length) -> index`, the same signature as in
JavaScript, so it transcribes directly:

```python
def south_slavic(choice, choices_length, _org=None):
    if choices_length == 2:
        return 0 if choice == 1 else 1
    ...

ctx = create_core_context(..., plural_rules={"sl": south_slavic})
translate(ctx, "trips", 103)     # '103 povezave' - the fourth form, as in the browser
```

If you pass **no** rule, you get vue-i18n's default, and that default ignores the locale: two forms
mean singular/plural, and anything more clamps at index 2, so a fourth or fifth form is unreachable
in any language. This port reproduces that rather than quietly improving on it — the same message
with no rule renders `3 povezavi`, `5 povezavi` and `105 povezavi` — the count interpolates, the
ending is what sticks — here and in the browser alike.

`vue_i18n.plurals` is there if you would rather not hand-write the rule. It is **opt-in**; the
runtime never reaches for it. It carries CLDR rules for 224 locales, an explicit `zero` form that
CLDR does not have, and a ladder for messages with fewer forms than the locale has categories. Note
that it will not agree with a hand-written rule in every case — if your front end has one, port that
one, and keep the two ends identical.

## Status

Early, and honest about it — see `PROGRESS.md` for what exists and `GAPS.md` for what does not.

## License

MIT
