finish-mds for full project #3
@@ -1 +1,240 @@
|
||||
# Add Your Language
|
||||
|
||||
Anyone can publish a `foreignthon-xx` language pack to PyPI — no access to the core repo is needed, and the pack works for anyone who installs it the moment it is on PyPI.
|
||||
|
||||
The recommended path is to start from the official language template. See [Contributing → Language Packs](../contributing/language-packs.md) for the full contribution and review process. This page is a quick reference.
|
||||
|
||||
---
|
||||
|
||||
## Start from the template
|
||||
|
||||
Fork the official language template on Gitea:
|
||||
|
||||
```
|
||||
https://git.keshavanand.net/foreign-thon/language-template.git
|
||||
```
|
||||
|
||||
The template gives you the full correct structure — `__init__.py`, JSON pack, the universal `tests/test_pack.py`, and all three CI workflows — pre-wired and ready to rename.
|
||||
|
||||
!!! tip "Why fork instead of starting fresh?"
|
||||
The template includes the shared test suite (`tests/test_pack.py`) that every official pack uses. Starting from it means your CI is already correct and your pack will pass the same checks as `foreignthon-es` and `foreignthon-ta` from day one.
|
||||
|
||||
Alternatively, if you just want a local pack for your own project without publishing, use:
|
||||
|
||||
```bash
|
||||
fpy new myproject --lang custom
|
||||
```
|
||||
|
||||
This scaffolds a `custom.json` locally — no PyPI account needed. See [Custom Packs](../custom-packs.md) for details.
|
||||
|
||||
---
|
||||
|
||||
## Package structure
|
||||
|
||||
After renaming the template for your language (e.g. French, code `fr`):
|
||||
|
||||
```
|
||||
foreignthon-fr/
|
||||
├── .gitea/workflows/
|
||||
│ ├── ci.yml # pytest on push / PR
|
||||
│ ├── publish.yml # PyPI upload on v* tag
|
||||
│ └── trigger-docs.yml # docs rebuild on README.md change
|
||||
├── .gitignore
|
||||
├── LICENSE # GPL v3
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
└── src/
|
||||
└── foreignthon_fr/
|
||||
├── __init__.py
|
||||
└── fr.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `pyproject.toml`
|
||||
|
||||
```toml
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "foreignthon-fr"
|
||||
version = "0.1.0"
|
||||
description = "French language pack for ForeignThon."
|
||||
license = { text = "GPL v3" }
|
||||
requires-python = ">=3.9"
|
||||
authors = [
|
||||
{ name = "Your Name", email = "you@example.com" }
|
||||
]
|
||||
keywords = ["foreignthon", "french", "français"]
|
||||
dependencies = ["foreignthon>=0.5.3"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.keshavanand.net/foreign-thon/foreignthon-fr"
|
||||
|
||||
[project.entry-points."foreignthon.langs"]
|
||||
fr = "foreignthon_fr"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/foreignthon_fr"]
|
||||
```
|
||||
|
||||
The `[project.entry-points."foreignthon.langs"]` block is what makes ForeignThon auto-discover your pack — the key is the language code, the value is the Python module name.
|
||||
|
||||
---
|
||||
|
||||
## `__init__.py`
|
||||
|
||||
```python
|
||||
from importlib.resources import files
|
||||
from importlib.metadata import version, metadata, PackageNotFoundError
|
||||
|
||||
try:
|
||||
package_name = (__package__ or "").replace("_", "-")
|
||||
__version__ = version(package_name)
|
||||
|
||||
pkg_metadata = metadata(package_name)
|
||||
raw_authors = pkg_metadata.get_all("Author") or []
|
||||
raw_emails = pkg_metadata.get_all("Author-email") or []
|
||||
|
||||
combined = []
|
||||
for item in (raw_authors + raw_emails):
|
||||
clean_name = item.split("<")[0].strip()
|
||||
if clean_name and clean_name not in combined:
|
||||
combined.append(clean_name)
|
||||
|
||||
__authors__ = combined
|
||||
|
||||
except PackageNotFoundError:
|
||||
__version__ = "0.0.0"
|
||||
__authors__ = []
|
||||
|
||||
|
||||
def get_pack_path():
|
||||
return files(__name__) / "fr.json"
|
||||
```
|
||||
|
||||
Change `"fr.json"` to match your actual filename.
|
||||
|
||||
---
|
||||
|
||||
## The JSON pack
|
||||
|
||||
Keys are your language's words. Values are English Python identifiers — **never change the values**.
|
||||
|
||||
```json
|
||||
{
|
||||
"meta": {
|
||||
"name": "French",
|
||||
"native_name": "Français",
|
||||
"code": "fr"
|
||||
},
|
||||
"keywords": {
|
||||
"si": "if",
|
||||
"sinon": "else",
|
||||
"sinonsi": "elif",
|
||||
"pour": "for",
|
||||
"tantque": "while",
|
||||
"déf": "def",
|
||||
"classe": "class",
|
||||
"importer": "import",
|
||||
"depuis": "from",
|
||||
"comme": "as",
|
||||
"retourner": "return",
|
||||
"arrêter": "break",
|
||||
"continuer": "continue",
|
||||
"passer": "pass",
|
||||
"essayer": "try",
|
||||
"sauf": "except",
|
||||
"finalement": "finally",
|
||||
"lever": "raise",
|
||||
"avec": "with",
|
||||
"dans": "in",
|
||||
"est": "is",
|
||||
"et": "and",
|
||||
"ou": "or",
|
||||
"non": "not",
|
||||
"supprimer": "del",
|
||||
"global": "global",
|
||||
"nonlocal": "nonlocal",
|
||||
"affirmer": "assert",
|
||||
"générer": "yield",
|
||||
"attendre": "await",
|
||||
"asynchrone": "async",
|
||||
"lambda": "lambda",
|
||||
"Vrai": "True",
|
||||
"Faux": "False",
|
||||
"Rien": "None"
|
||||
},
|
||||
"builtins": {
|
||||
"afficher": "print",
|
||||
"saisir": "input",
|
||||
"longueur": "len",
|
||||
"intervalle": "range",
|
||||
...
|
||||
},
|
||||
"exceptions": {
|
||||
"ErreurDeValeur": "ValueError",
|
||||
...
|
||||
},
|
||||
"error_messages": {
|
||||
"ValueError": "Erreur de valeur",
|
||||
...
|
||||
},
|
||||
"stdlib": {
|
||||
"mathématiques": "math",
|
||||
...
|
||||
},
|
||||
"postfix_keywords": []
|
||||
}
|
||||
```
|
||||
|
||||
### `postfix_keywords`
|
||||
|
||||
Set this to a list of English keywords if your language is SOV (subject-object-verb) and benefits from `@@` postfix style on decompile. Example (Tamil):
|
||||
|
||||
```json
|
||||
"postfix_keywords": ["if", "elif", "while", "class", "with"]
|
||||
```
|
||||
|
||||
Set it to `[]` for SVO languages like Spanish or French.
|
||||
|
||||
### Filename rule
|
||||
|
||||
The JSON filename **must** match `meta.code` exactly. `fr.json` for code `"fr"`.
|
||||
|
||||
---
|
||||
|
||||
## Validate and test
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
pip install pytest
|
||||
|
||||
fpy pack src/foreignthon_fr/fr.json
|
||||
# ✓ Pack 'French' is valid.
|
||||
|
||||
pytest tests/ -v
|
||||
```
|
||||
|
||||
The shared `tests/test_pack.py` checks that all sections exist, all values are real Python identifiers, the filename matches `meta.code`, and `postfix_keywords` entries each have a translation.
|
||||
|
||||
---
|
||||
|
||||
## Publish
|
||||
|
||||
Tag and push — the CI handles the rest:
|
||||
|
||||
```bash
|
||||
git tag v0.1.0
|
||||
git push origin v0.1.0
|
||||
```
|
||||
|
||||
The `publish.yml` workflow builds the wheel and uploads it to PyPI using the `PYPI_TOKEN` secret. Add your own `PYPI_TOKEN` to your fork's secrets if you are not yet under the official org.
|
||||
|
||||
---
|
||||
|
||||
## Getting listed
|
||||
|
||||
Once your pack is working and on PyPI, open a PR or issue on [foreignthon-docs](https://git.keshavanand.net/foreign-thon/foreignthon-docs) to get added to the [Language Packs overview](index.md). See [Contributing → Language Packs](../contributing/language-packs.md) for the full process.
|
||||
|
||||
Reference in New Issue
Block a user