Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion mempalace/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,17 @@ class Dialect:
dialect.generate_layer1("zettels/", output="LAYER1.aaak")
"""

def __init__(self, entities: Dict[str, str] = None, skip_names: List[str] = None):
def __init__(
self, entities: Dict[str, str] = None, skip_names: List[str] = None, lang: str = None
):
"""
Args:
entities: Mapping of full names -> short codes.
e.g. {"Alice": "ALC", "Bob": "BOB"}
If None, entities are auto-coded from first 3 chars.
skip_names: Names to skip (fictional characters, etc.)
lang: Language code (e.g. "fr", "ko"). Loads AAAK instruction
and regex patterns from i18n dictionary.
"""
self.entity_codes = {}
if entities:
Expand All @@ -332,6 +336,15 @@ def __init__(self, entities: Dict[str, str] = None, skip_names: List[str] = None
self.entity_codes[name.lower()] = code
self.skip_names = [n.lower() for n in (skip_names or [])]

# Load language-specific AAAK instruction and regex patterns
from mempalace.i18n import load_lang, t, current_lang, get_regex

if lang:
load_lang(lang)
self.lang = lang or current_lang()
self.aaak_instruction = t("aaak.instruction")
self.lang_regex = get_regex()

@classmethod
def from_config(cls, config_path: str) -> "Dialect":
"""Load entity mappings from a JSON config file.
Expand All @@ -347,6 +360,7 @@ def from_config(cls, config_path: str) -> "Dialect":
return cls(
entities=config.get("entities", {}),
skip_names=config.get("skip_names", []),
lang=config.get("lang"),
)

def save_config(self, config_path: str):
Expand Down
76 changes: 76 additions & 0 deletions mempalace/i18n/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""i18n — Language dictionaries for MemPalace.

Usage:
from mempalace.i18n import load_lang, t

load_lang("fr") # load French
print(t("cli.mine_start", path="/docs")) # "Extraction de /docs..."
print(t("terms.wing")) # "aile"
print(t("aaak.instruction")) # AAAK compression instruction in French
"""

import json
from pathlib import Path

_LANG_DIR = Path(__file__).parent
_strings: dict = {}
_current_lang: str = "en"


def available_languages() -> list[str]:
"""Return list of available language codes."""
return sorted(p.stem for p in _LANG_DIR.glob("*.json"))


def load_lang(lang: str = "en") -> dict:
"""Load a language dictionary. Falls back to English if not found."""
global _strings, _current_lang
lang_file = _LANG_DIR / f"{lang}.json"
if not lang_file.exists():
lang_file = _LANG_DIR / "en.json"
lang = "en"
_strings = json.loads(lang_file.read_text(encoding="utf-8"))
_current_lang = lang
return _strings


def t(key: str, **kwargs) -> str:
"""Get a translated string by dotted key. Supports {var} interpolation.

t("cli.mine_complete", closets=5, drawers=20)
→ "Done. 5 closets, 20 drawers created."
"""
if not _strings:
load_lang("en")
parts = key.split(".", 1)
if len(parts) == 2:
section, name = parts
val = _strings.get(section, {}).get(name, key)
else:
val = _strings.get(key, key)
if kwargs and isinstance(val, str):
try:
val = val.format(**kwargs)
except (KeyError, IndexError):
pass
return val


def current_lang() -> str:
"""Return current language code."""
return _current_lang


def get_regex() -> dict:
"""Return the regex patterns for the current language.

Keys: topic_pattern, stop_words, quote_pattern, action_pattern.
Returns empty dict if no regex section in the language file.
"""
if not _strings:
load_lang("en")
return _strings.get("regex", {})


# Auto-load English on import
load_lang("en")
44 changes: 44 additions & 0 deletions mempalace/i18n/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "de",
"label": "Deutsch",
"terms": {
"palace": "Palast",
"wing": "Flügel",
"hall": "Flur",
"closet": "Schrank",
"drawer": "Schublade",
"mine": "schürfen",
"search": "suchen",
"status": "Status",
"init": "initialisieren",
"repair": "reparieren",
"migrate": "migrieren",
"entity": "Entität",
"topic": "Thema"
},
"cli": {
"mine_start": "Schürfe {path}...",
"mine_complete": "Fertig. {closets} Schränke, {drawers} Schubladen erstellt.",
"mine_skip": "Bereits geschürft. Verwenden Sie --force zum Wiederholen.",
"search_no_results": "Keine Ergebnisse für: {query}",
"search_results": "{count} Ergebnisse gefunden:",
"status_palace": "Palast: {path}",
"status_wings": "{count} Flügel",
"status_closets": "{count} Schränke",
"status_drawers": "{count} Schubladen",
"init_complete": "Palast initialisiert in {path}",
"init_exists": "Palast existiert bereits in {path}",
"repair_complete": "Reparatur abgeschlossen. {fixed} Probleme behoben.",
"migrate_complete": "Migration abgeschlossen.",
"no_palace": "Kein Palast gefunden. Ausführen: mempalace init <Ordner>"
},
"aaak": {
"instruction": "Auf Deutsch komprimieren. Bindestriche zwischen Wörtern, Pipes zwischen Konzepten. Artikel und Füllwörter weglassen. Eigennamen und Zahlen exakt beibehalten."
},
"regex": {
"topic_pattern": "[A-ZÄÖÜß][a-zäöüß]{2,}|[A-Za-zÄÖÜäöüß]{3,}",
"stop_words": "der die das ein eine eines einer einem einen den dem des und oder aber denn weil wenn als ob auch noch schon sehr viel nur nicht mehr kann wird hat ist sind war waren sein haben wurde mit von zu für auf in an um über nach durch",
"quote_pattern": "\\u201E([^\\u201C]{10,200})\\u201C|\"([^\"]{10,200})\"",
"action_pattern": "(?:gebaut|behoben|geschrieben|hinzugefügt|gepusht|gemessen|getestet|überprüft|erstellt|gelöscht|aktualisiert|konfiguriert|bereitgestellt|migriert)\\s+[\\wÄÖÜäöüß\\s]{3,30}"
}
}
44 changes: 44 additions & 0 deletions mempalace/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "en",
"label": "English",
"terms": {
"palace": "palace",
"wing": "wing",
"hall": "hall",
"closet": "closet",
"drawer": "drawer",
"mine": "mine",
"search": "search",
"status": "status",
"init": "init",
"repair": "repair",
"migrate": "migrate",
"entity": "entity",
"topic": "topic"
},
"cli": {
"mine_start": "Mining {path}...",
"mine_complete": "Done. {closets} closets, {drawers} drawers created.",
"mine_skip": "Already mined. Use --force to re-mine.",
"search_no_results": "No results for: {query}",
"search_results": "Found {count} results:",
"status_palace": "Palace: {path}",
"status_wings": "{count} wings",
"status_closets": "{count} closets",
"status_drawers": "{count} drawers",
"init_complete": "Palace initialized at {path}",
"init_exists": "Palace already exists at {path}",
"repair_complete": "Repair complete. {fixed} issues fixed.",
"migrate_complete": "Migration complete.",
"no_palace": "No palace found. Run: mempalace init <dir>"
},
"aaak": {
"instruction": "Compress to index format. Hyphens between words, pipes between concepts. Drop articles and filler. Keep names and numbers exact."
},
"regex": {
"topic_pattern": "[A-Z][a-z]{2,}|[A-Za-z][A-Za-z0-9_]{2,}",
"stop_words": "the this that these those some many most each every other only such very will would could should must shall yeah okay also even then now already still back done make take give know think want need going come find work added saved session summary conversation topics source about once just really actually here there where good great better thank please sorry right wrong true false",
"quote_pattern": "\"([^\"]{20,200})\"",
"action_pattern": "(?:built|fixed|wrote|added|pushed|measured|tested|reviewed|created|deleted|updated|configured|deployed|migrated)\\s+[\\w\\s]{3,30}"
}
}
44 changes: 44 additions & 0 deletions mempalace/i18n/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "es",
"label": "Español",
"terms": {
"palace": "palacio",
"wing": "ala",
"hall": "pasillo",
"closet": "armario",
"drawer": "cajón",
"mine": "extraer",
"search": "buscar",
"status": "estado",
"init": "inicializar",
"repair": "reparar",
"migrate": "migrar",
"entity": "entidad",
"topic": "tema"
},
"cli": {
"mine_start": "Extrayendo {path}...",
"mine_complete": "Listo. {closets} armarios, {drawers} cajones creados.",
"mine_skip": "Ya extraído. Use --force para repetir.",
"search_no_results": "Sin resultados para: {query}",
"search_results": "{count} resultados encontrados:",
"status_palace": "Palacio: {path}",
"status_wings": "{count} alas",
"status_closets": "{count} armarios",
"status_drawers": "{count} cajones",
"init_complete": "Palacio inicializado en {path}",
"init_exists": "Ya existe un palacio en {path}",
"repair_complete": "Reparación completa. {fixed} problemas corregidos.",
"migrate_complete": "Migración completa.",
"no_palace": "No se encontró palacio. Ejecute: mempalace init <carpeta>"
},
"aaak": {
"instruction": "Comprima en español. Guiones entre palabras, pipes entre conceptos. Elimine artículos y palabras de relleno. Mantenga nombres propios y números exactos."
},
"regex": {
"topic_pattern": "[A-ZÁ-Ú][a-zá-ú]{2,}|[A-Za-zÁ-ú]{3,}",
"stop_words": "el la los las un una unos unas de del al en con por para su sus mi mis tu tus es son está están fue ser estar haber sido como pero más muy también todo todos toda todas este esta estos estas ese esa esos esas que quien cual donde cuando porque aunque sin",
"quote_pattern": "\"([^\"]{10,200})\"|«([^»]{10,200})»",
"action_pattern": "(?:construido|corregido|escrito|añadido|enviado|medido|probado|revisado|creado|eliminado|actualizado|configurado|desplegado|migrado)\\s+[\\wá-ú\\s]{3,30}"
}
}
44 changes: 44 additions & 0 deletions mempalace/i18n/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "fr",
"label": "Français",
"terms": {
"palace": "palais",
"wing": "aile",
"hall": "couloir",
"closet": "placard",
"drawer": "tiroir",
"mine": "extraire",
"search": "chercher",
"status": "état",
"init": "initialiser",
"repair": "réparer",
"migrate": "migrer",
"entity": "entité",
"topic": "sujet"
},
"cli": {
"mine_start": "Extraction de {path}...",
"mine_complete": "Terminé. {closets} placards, {drawers} tiroirs créés.",
"mine_skip": "Déjà extrait. Utilisez --force pour refaire.",
"search_no_results": "Aucun résultat pour : {query}",
"search_results": "{count} résultats trouvés :",
"status_palace": "Palais : {path}",
"status_wings": "{count} ailes",
"status_closets": "{count} placards",
"status_drawers": "{count} tiroirs",
"init_complete": "Palais initialisé dans {path}",
"init_exists": "Un palais existe déjà dans {path}",
"repair_complete": "Réparation terminée. {fixed} problèmes corrigés.",
"migrate_complete": "Migration terminée.",
"no_palace": "Aucun palais trouvé. Exécutez : mempalace init <dossier>"
},
"aaak": {
"instruction": "Comprimez en français. Tirets entre les mots, pipes entre les concepts. Supprimez les articles et mots de remplissage. Gardez les noms propres et chiffres exacts."
},
"regex": {
"topic_pattern": "[A-ZÀ-Ý][a-zà-ÿ]{2,}|[A-Za-zÀ-ÿ]{3,}",
"stop_words": "le la les un une des de du au aux en et ou mais donc or ni car que qui ce cette ces son sa ses mon ma mes ton ta tes leur leurs nous vous ils elles on ne pas plus très bien aussi avec pour dans sur par est sont fait être avoir été comme tout tous toute toutes",
"quote_pattern": "«\\s*([^»]{10,200})\\s*»|\"([^\"]{10,200})\"",
"action_pattern": "(?:construit|corrigé|écrit|ajouté|poussé|mesuré|testé|révisé|créé|supprimé|mis à jour|configuré|déployé|migré)\\s+[\\wà-ÿ\\s]{3,30}"
}
}
44 changes: 44 additions & 0 deletions mempalace/i18n/ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "ja",
"label": "日本語",
"terms": {
"palace": "宮殿",
"wing": "棟",
"hall": "廊下",
"closet": "クローゼット",
"drawer": "引き出し",
"mine": "採掘",
"search": "検索",
"status": "状態",
"init": "初期化",
"repair": "修復",
"migrate": "移行",
"entity": "エンティティ",
"topic": "トピック"
},
"cli": {
"mine_start": "{path} を採掘中...",
"mine_complete": "完了。クローゼット {closets}個、引き出し {drawers}個 作成。",
"mine_skip": "採掘済み。再実行するには --force を使用。",
"search_no_results": "結果なし: {query}",
"search_results": "{count}件の結果:",
"status_palace": "宮殿: {path}",
"status_wings": "棟 {count}個",
"status_closets": "クローゼット {count}個",
"status_drawers": "引き出し {count}個",
"init_complete": "{path} に宮殿を初期化しました",
"init_exists": "{path} に宮殿は既に存在します",
"repair_complete": "修復完了。{fixed}件の問題を修正。",
"migrate_complete": "移行完了。",
"no_palace": "宮殿が見つかりません。実行: mempalace init <ディレクトリ>"
},
"aaak": {
"instruction": "日本語で圧縮してください。概念間はパイプ(|)、単語間はハイフン(-)。助詞と接続詞は省略。固有名詞と数値は正確に保持。"
},
"regex": {
"topic_pattern": "[\\u30A0-\\u30FF]{3,}|[\\u4E00-\\u9FFF]{2,}|[A-Za-z][A-Za-z0-9_]{2,}",
"stop_words": "は が を に で と も の へ から まで より した します している されて です ます ました こと もの ため それ これ その この あの ない なく ある いる する",
"quote_pattern": "「([^」]{10,100})」",
"action_pattern": "(構築|修正|追加|削除|確認|作成|実装|修復|書き直し|テスト|検証|更新|設定|起動|停止)(?:し|した|して|する|します)"
}
}
44 changes: 44 additions & 0 deletions mempalace/i18n/ko.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"lang": "ko",
"label": "한국어",
"terms": {
"palace": "궁전",
"wing": "날개",
"hall": "복도",
"closet": "벽장",
"drawer": "서랍",
"mine": "채굴",
"search": "검색",
"status": "상태",
"init": "초기화",
"repair": "수리",
"migrate": "마이그레이션",
"entity": "개체",
"topic": "주제"
},
"cli": {
"mine_start": "{path} 채굴 중...",
"mine_complete": "완료. 벽장 {closets}개, 서랍 {drawers}개 생성.",
"mine_skip": "이미 채굴됨. --force로 다시 실행하세요.",
"search_no_results": "결과 없음: {query}",
"search_results": "{count}개 결과 발견:",
"status_palace": "궁전: {path}",
"status_wings": "날개 {count}개",
"status_closets": "벽장 {count}개",
"status_drawers": "서랍 {drawers}개",
"init_complete": "{path}에 궁전 초기화 완료",
"init_exists": "{path}에 궁전이 이미 존재합니다",
"repair_complete": "수리 완료. {fixed}개 문제 해결.",
"migrate_complete": "마이그레이션 완료.",
"no_palace": "궁전을 찾을 수 없습니다. 실행: mempalace init <폴더>"
},
"aaak": {
"instruction": "한국어로 압축하세요. 개념 사이에 파이프(|), 단어 연결에 하이픈(-). 조사와 접속사는 생략. 고유명사와 숫자는 정확히 유지."
},
"regex": {
"topic_pattern": "[가-힣]{2,}|[A-Za-z][A-Za-z0-9_]{2,}",
"stop_words": "은 는 이 가 을 를 에 에서 의 로 으로 와 과 도 만 까지 부터 처럼 보다 한 하는 했다 합니다 했습니다 되었 있는 것 수 등 및 또는 그리고 하지만 때문에",
"quote_pattern": "\"([^\"]{10,100})\"|'([^']{10,100})'",
"action_pattern": "(구축|수정|추가|삭제|확인|생성|구현|수리|작성|테스트|검증|업데이트|설정|시작|중지)(?:했|한|하여|합니다|했습니다)"
}
}
Loading