added new translations for custom fields
This commit is contained in:
parent
e88ab56df0
commit
30c46e2feb
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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ə")
|
||||
Loading…
Reference in New Issue