Files
Budget_Tool_Martinhal/backend/app/reports/i18n.py
T

137 lines
5.5 KiB
Python
Raw Normal View History

2026-10-29 20:40:02 +00:00
from decimal import Decimal
LABELS = {
"en": {
"cost_report_title": "Capex and Costs {year}",
"cost_report_subtitle": "{dept} · cost budget: {prior} actual vs. {year} with adjustment",
"total_across": "Total across {n} categories",
"by_category": "Budget by category",
"by_category_sub": "Total cost per category, {prior} vs. {year}",
"change_by_category": "Change by category",
"change_by_category_sub": "Percentage change from {prior} actual to {year} with adjustment",
"change_legend": "Orange = cost increase · Blue = cost reduction",
"prior_actual": "{prior} actual",
"adjusted": "{year} with adjustment",
"base": "{year} without adjustment",
"change_eur": "Change (€)",
"change_pct": "Change (%)",
"cost_lines": "{n} cost lines",
"cost_line": "1 cost line",
"summary": "Summary",
"summary_sub": "All categories, {prior} actual vs. {year} with adjustment",
"category": "Category",
"line": "Line",
"site": "Site",
"group": "Group",
"total": "Total",
"factor": "Factor",
"reason": "Reason",
"remarks": "Remarks",
"budget_title": "Budget {year} · {dept}",
"budget_sub": "Monthly budget by category",
"revenue_total": "Sub total revenue",
"sub_total": "Sub total {name}",
"total_costs": "Total costs",
"status": "Status",
"exported": "Exported",
"draft": "Draft",
"in_review": "In review",
"approved": "Approved",
"inputs": "Inputs",
"months": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"no_data": "No cost lines yet.",
"site_filter": "Site: {site}",
},
"pt": {
"cost_report_title": "Capex e Custos para {year}",
"cost_report_subtitle": "{dept} · orçamento de custos: real {prior} vs. {year} com ajuste",
"total_across": "Total de {n} categorias",
"by_category": "Orçamento por categoria",
"by_category_sub": "Custo total por categoria, {prior} vs. {year}",
"change_by_category": "Variação por categoria",
"change_by_category_sub": "Variação percentual do real {prior} para {year} com ajuste",
"change_legend": "Laranja = aumento de custo · Azul = redução de custo",
"prior_actual": "{prior} real",
"adjusted": "{year} com ajuste",
"base": "{year} sem ajuste",
"change_eur": "Variação (€)",
"change_pct": "Variação (%)",
"cost_lines": "{n} linhas de custo",
"cost_line": "1 linha de custo",
"summary": "Resumo",
"summary_sub": "Todas as categorias, real {prior} vs. {year} com ajuste",
"category": "Categoria",
"line": "Linha",
"site": "Unidade",
"group": "Grupo",
"total": "Total",
"factor": "Fator",
"reason": "Motivo",
"remarks": "Observações",
"budget_title": "Orçamento {year} · {dept}",
"budget_sub": "Orçamento mensal por categoria",
"revenue_total": "Subtotal receitas",
"sub_total": "Subtotal {name}",
"total_costs": "Total de custos",
"status": "Estado",
"exported": "Exportado",
"draft": "Rascunho",
"in_review": "Em revisão",
"approved": "Aprovado",
"inputs": "Dados",
"months": ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
"no_data": "Ainda não há linhas de custo.",
"site_filter": "Unidade: {site}",
},
}
class T:
def __init__(self, lang: str, year: int, dept: str = ""):
self.lang = lang if lang in LABELS else "pt"
self.labels = LABELS[self.lang]
self.ctx = {"year": year, "prior": year - 1, "dept": dept}
def __call__(self, key: str, **kw):
value = self.labels[key]
return value.format(**{**self.ctx, **kw}) if isinstance(value, str) else value
def _group(self, value: Decimal, decimals: int) -> str:
q = Decimal(1).scaleb(-decimals) if decimals else Decimal(1)
text = f"{abs(Decimal(value)).quantize(q):,.{decimals}f}"
whole, _, frac = text.partition(".")
whole = whole.replace(",", " ")
if not frac:
return whole
return whole + ("," if self.lang == "pt" else ".") + frac
def eur(self, value, decimals: int = 2, signed: bool = False) -> str:
value = Decimal(value or 0)
sign = ("+" if value > 0 else "−" if value < 0 else "") if signed else ("−" if value < 0 else "")
body = self._group(value, decimals)
return f"{sign}{body} €" if self.lang == "pt" else f"{sign}€{body}"
def num(self, value, decimals: int = 2) -> str:
value = Decimal(value or 0)
return ("\u2212" if value < 0 else "") + self._group(value, decimals)
def pct(self, value, signed: bool = True) -> str:
if value is None:
return "—"
value = Decimal(value)
sign = ("+" if value > 0 else "−" if value < 0 else "") if signed else ""
return f"{sign}{self._group(value, 2)}%"
def short_eur(self, value: float) -> str:
v = abs(value)
sign = "−" if value < 0 else ""
if v >= 1_000_000:
body = f"{v / 1_000_000:g}M"
elif v >= 1000:
body = f"{v / 1000:g}k"
else:
body = f"{v:g}"
if self.lang == "pt":
return f"{sign}{body.replace('.', ',')} €"
return f"{sign}€{body}"