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

137 lines
5.5 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}"