added new translations for custom fields

This commit is contained in:
Ali 2026-05-25 13:32:29 +00:00
parent e88ab56df0
commit 30c46e2feb
4 changed files with 2832 additions and 3 deletions

View File

@ -100,6 +100,12 @@ after_migrate = "jey_erp.hooks.after_migrate_combined"
def after_migrate_combined():
# Regenerate translation_markers.py from custom_fields.py before anything
# else, so a fresh marker file is on disk if the next step is
# `bench generate-pot-file`. No runtime side effects.
from jey_erp.translate import sync_translation_markers
sync_translation_markers()
from jey_erp.custom_fields import create_custom_fields
create_custom_fields()
from jey_erp.custom.show_item_tax_template_in_sales_invoice import show_item_tax_template_in_sales_invoice

File diff suppressed because it is too large Load Diff

147
jey_erp/translate.py Normal file
View File

@ -0,0 +1,147 @@
"""Translation marker management for Custom Fields.
Custom Field labels and descriptions defined in `custom_fields.py` are bare
Python strings inside `dict(label='...', description='...')` literals. Frappe's
gettext extractor (`bench generate-pot-file`) scans .py files only for
`__()`/`_()`/`_lt()` calls it cannot see bare strings inside dict literals.
Result: those labels never make it into `jey_erp.pot`, then into `.po`, then
into runtime translations even though Frappe DOES wrap them in `__()` when
rendering Custom Field labels at form render time.
This module ASTs `custom_fields.py`, harvests every string value of the
`label` and `description` keyword arguments, and writes them out to
`translation_markers.py` as `_lt(...)` calls. `_lt` is a lazy translation
proxy never rendered, never called for real but the gettext extractor
recognizes it the same as `_`/`__`, so the strings show up in the POT.
Triggered automatically at the top of `after_migrate_combined()` in `hooks.py`,
or manually via:
bench execute jey_erp.translate.sync_translation_markers
"""
import ast
import json
import os
SOURCE_FILE = "custom_fields.py"
MARKER_FILE = "translation_markers.py"
EXTRACTED_KWARGS = ("label", "description")
_HEADER = '''\
# AUTO-GENERATED FILE — DO NOT EDIT BY HAND.
#
# Regenerated from {source} by jey_erp.translate.sync_translation_markers,
# invoked at the top of the after_migrate hook in hooks.py.
#
# Purpose: expose Custom Field labels/descriptions to bench generate-pot-file.
# The extractor scans .py files for _lt()/_()/__() calls only; it cannot see
# bare strings inside dict(label='...') literals. _lt() is a lazy translation
# proxy with no runtime effect — these calls exist purely so gettext picks
# the strings up.
from frappe import _lt
'''
def sync_translation_markers():
"""Regenerate translation_markers.py from labels in custom_fields.py.
Idempotent: skips writing if the regenerated content matches what's already
on disk. Safe to call repeatedly.
"""
app_dir = os.path.dirname(os.path.abspath(__file__))
source_path = os.path.join(app_dir, SOURCE_FILE)
marker_path = os.path.join(app_dir, MARKER_FILE)
if not os.path.isfile(source_path):
print(f"[jey_erp] sync_translation_markers: {source_path} not found, skipping")
return
try:
with open(source_path, encoding="utf-8") as f:
tree = ast.parse(f.read(), filename=SOURCE_FILE)
except SyntaxError as e:
print(f"[jey_erp] sync_translation_markers: failed to parse {SOURCE_FILE}: {e}")
return
labels = _extract_strings(tree)
new_content = _render(labels)
if os.path.exists(marker_path):
try:
with open(marker_path, encoding="utf-8") as f:
if f.read() == new_content:
print(
f"[jey_erp] sync_translation_markers: {MARKER_FILE} up to date "
f"({len(labels)} markers)"
)
return
except OSError as e:
print(f"[jey_erp] sync_translation_markers: could not read existing {MARKER_FILE}: {e}")
try:
with open(marker_path, "w", encoding="utf-8") as f:
f.write(new_content)
except OSError as e:
print(f"[jey_erp] sync_translation_markers: failed to write {MARKER_FILE}: {e}")
return
print(
f"[jey_erp] sync_translation_markers: regenerated {MARKER_FILE} "
f"({len(labels)} markers)"
)
def _extract_strings(tree):
"""Walk the AST and harvest string values for `label=` / `description=`.
Handles both `dict(label='X', ...)` constructor form and `{'label': 'X', ...}`
literal form. Skips non-string values (e.g. f-strings, variables).
"""
found = set()
for node in ast.walk(tree):
# dict(label='X', description='Y', ...)
if (
isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "dict"
):
for kw in node.keywords:
if kw.arg in EXTRACTED_KWARGS and _is_str_constant(kw.value):
val = kw.value.value.strip()
if val:
found.add(val)
# {"label": "X", "description": "Y", ...}
elif isinstance(node, ast.Dict):
for key, value in zip(node.keys, node.values):
if (
isinstance(key, ast.Constant)
and isinstance(key.value, str)
and key.value in EXTRACTED_KWARGS
and _is_str_constant(value)
):
val = value.value.strip()
if val:
found.add(val)
return sorted(found)
def _is_str_constant(node):
return isinstance(node, ast.Constant) and isinstance(node.value, str)
def _render(labels):
lines = [_HEADER.format(source=SOURCE_FILE)]
for label in labels:
# json.dumps produces a valid Python string literal: handles quotes,
# backslashes, and newlines correctly. ensure_ascii=False keeps
# Cyrillic / Azerbaijani / etc. readable in the generated file.
lines.append(f"_lt({json.dumps(label, ensure_ascii=False)})")
lines.append("")
return "\n".join(lines)

View File

@ -0,0 +1,191 @@
# AUTO-GENERATED FILE — DO NOT EDIT BY HAND.
#
# Regenerated from custom_fields.py by jey_erp.translate.sync_translation_markers,
# invoked at the top of the after_migrate hook in hooks.py.
#
# Purpose: expose Custom Field labels/descriptions to bench generate-pot-file.
# The extractor scans .py files for _lt()/_()/__() calls only; it cannot see
# bare strings inside dict(label='...') literals. _lt() is a lazy translation
# proxy with no runtime effect — these calls exist purely so gettext picks
# the strings up.
from frappe import _lt
_lt("A temporary employee starts working from")
_lt("A temporary employee starts working to")
_lt("Absence Reason")
_lt("Act Kind")
_lt("Act Type")
_lt("Actual Address (Full)")
_lt("Additional Activity Types")
_lt("Additional Comment")
_lt("Address Information")
_lt("Address for Mail")
_lt("Affiliate Organizations")
_lt("Agricultural Land Information")
_lt("Amount")
_lt("Amount without VAT")
_lt("Area")
_lt("Asset Type")
_lt("Ata adı")
_lt("Auto-calculated as 5% of amount when Tax Type is Taxable")
_lt("Bank Accounts")
_lt("Bank Code")
_lt("Bank Integration")
_lt("Bank Integration Type")
_lt("Business Activities")
_lt("Business Classification")
_lt("Calculated as: VAT 18% with amount - Amount")
_lt("Cash Registers")
_lt("Chief Executive Officer")
_lt("Citizenship Country")
_lt("City (for print formats)")
_lt("Code of the Cadastral Valuation District")
_lt("Code of the Territorial Unit")
_lt("Comment")
_lt("Comments")
_lt("Common Information")
_lt("Company Main Activity")
_lt("Contact Information (E-Taxes)")
_lt("Correspondent account (m/h)")
_lt("Country")
_lt("Customer Object Name")
_lt("Date of Birth")
_lt("Default Landed Cost Account")
_lt("Description")
_lt("Director")
_lt("Director Name (E-Taxes)")
_lt("Director PIN")
_lt("E-Taxes Document Type")
_lt("E-Taxes Individual Information")
_lt("E-Taxes Integration")
_lt("E-Taxes Invoice ID")
_lt("E-Taxes Objects")
_lt("E-Taxes Purchase Act")
_lt("E-Taxes Purchase Act ID")
_lt("E-Taxes Send Status")
_lt("E-Taxes Serial Number")
_lt("E-Taxes Status")
_lt("E-Taxes Verification Code")
_lt("EQM Code")
_lt("Employee Count")
_lt("Employer Information")
_lt("Employer Name")
_lt("Employer Position")
_lt("Enter the tax-exempt area value")
_lt("Expense/Income")
_lt("FIN")
_lt("First Name")
_lt("For agricultural: hectares. For industrial: square meters.")
_lt("FİN")
_lt("Get Items from Vendor Invoices")
_lt("Goods provided instead of salary")
_lt("Göstəricilər")
_lt("Has Active Production Object")
_lt("Industrial/Commercial Land Information")
_lt("Is Account")
_lt("Is Chief of Any Legal Entity")
_lt("Is Risky Taxpayer")
_lt("Is Standard")
_lt("Is Sub Account")
_lt("Is Taxpayer in Cancellation Process")
_lt("Is taxes document")
_lt("Item")
_lt("Job Applicant")
_lt("Kassa metodu — gəlir və xərclər yalnız ödəniş zamanı tanınır. ƏDV öhdəliyi yalnız ödəniş alındıqda yaranır.\nHesablama metodu — gəlir və xərclər faktura zamanı tanınır.")
_lt("Land")
_lt("Landline Phone")
_lt("Last Name")
_lt("Legal Address (Full)")
_lt("Legal Address House Number")
_lt("Legal Address Locality")
_lt("Legal Address Postcode")
_lt("Legal Address Region")
_lt("Legal Address Room Number")
_lt("Legal Address Street")
_lt("Legal Form Code")
_lt("Liquidation Date")
_lt("List of tax systems applied")
_lt("Loaded from E-Taxes")
_lt("MDSS")
_lt("MDSS üzrə")
_lt("Main organization from E-Taxes")
_lt("Main type of activity")
_lt("Management Information")
_lt("Mobile Phone")
_lt("Name of the Cadastral Valuation District")
_lt("Name of the Territorial Unit")
_lt("Not Available")
_lt("Note")
_lt("Note on Mining")
_lt("Order number")
_lt("Organizational Structure")
_lt("Organizer")
_lt("POS Terminals")
_lt("Parent Organization")
_lt("Parent Organization TIN")
_lt("Passport Serial Number")
_lt("Payment account (h/h)")
_lt("Phone Number")
_lt("Presented Certificates")
_lt("Product Category")
_lt("Product Group Code")
_lt("Property")
_lt("Property Type")
_lt("Purchase Tax Amount (5%)")
_lt("Purchase Type")
_lt("Purpose")
_lt("Purpose of the Land Plot")
_lt("Quality Groups")
_lt("Reason")
_lt("Registration Information")
_lt("Residence Permit FIN")
_lt("SSN")
_lt("Selected customer object from E-Taxes (auto-filled)")
_lt("Seller")
_lt("Settlements")
_lt("Special Tax Regime")
_lt("Sport Betting Operator")
_lt("State Registration Authority")
_lt("State Registration Document Issued Date")
_lt("Suspension End Date")
_lt("Suspension Start Date")
_lt("TIN Type")
_lt("Tax Article")
_lt("Tax Article field for VAT purposes")
_lt("Tax Authority")
_lt("Tax Closing Wizards")
_lt("Tax Exempt Assets Information")
_lt("Tax Free")
_lt("Tax Free Amount")
_lt("Tax Information")
_lt("Tax Policy")
_lt("Tax System Type")
_lt("Tax Systems")
_lt("Tax Systems List")
_lt("Tax Type")
_lt("Tax regime for Tax Inspector audit calculations")
_lt("Tax-exempt Area")
_lt("Taxable Asset Type")
_lt("Taxable Assets Information")
_lt("Taxation system")
_lt("Taxpayer Activity Group")
_lt("Type of act for e-taxes")
_lt("Used as the Expense Account when this service item is selected in a Landed Cost Voucher.")
_lt("Uçot metodu (Accounting Method)")
_lt("VAT 0% with amount")
_lt("VAT 18% with amount")
_lt("VAT Amount")
_lt("VAT Information")
_lt("VAT certificate date")
_lt("VAT certificate number")
_lt("VAT free amount")
_lt("VAT registration date")
_lt("Vergi")
_lt("Wizards")
_lt("taxes_doc")
_lt("İcbari Tibbi Sığorta")
_lt("İcbari tibbi sığorta üzrə")
_lt("İşsizlik sığorta")
_lt("İşsizlikdən sığorta üzrə")