[I18N] website_sale_aplicoop: Limpieza de traducciones y etiquetas UI en inglés por defecto

This commit is contained in:
snt 2026-02-26 14:33:44 +01:00
parent c2f9f347b7
commit 9937e987f4
12 changed files with 215 additions and 290877 deletions

View file

@ -116,7 +116,7 @@ repos:
- id: mypy - id: mypy
# do not run on test files or __init__ files (mypy does not support # do not run on test files or __init__ files (mypy does not support
# namespace packages) # namespace packages)
exclude: (/tests/|/__init__\.py$) exclude: (/tests/|/__init__\.py$|^scripts/)
# Exclude migrations explicitly to avoid duplicate-module errors # Exclude migrations explicitly to avoid duplicate-module errors
args: ["--exclude", "(?i).*/migrations/.*"] args: ["--exclude", "(?i).*/migrations/.*"]
additional_dependencies: additional_dependencies:
@ -135,10 +135,12 @@ repos:
- --rcfile=.pylintrc - --rcfile=.pylintrc
- --exit-zero - --exit-zero
verbose: true verbose: true
exclude: ^scripts/
additional_dependencies: &pylint_deps additional_dependencies: &pylint_deps
- pylint-odoo==10.0.0 - pylint-odoo==10.0.0
- id: pylint - id: pylint
name: pylint with mandatory checks name: pylint with mandatory checks
args: args:
- --rcfile=.pylintrc-mandatory - --rcfile=.pylintrc-mandatory
exclude: ^scripts/
additional_dependencies: *pylint_deps additional_dependencies: *pylint_deps

View file

@ -90,6 +90,7 @@ class MyModel(models.Model):
### 1. Exportar Términos Traducibles ### 1. Exportar Términos Traducibles
NO HACER:
```bash ```bash
# Exportar términos del addon # Exportar términos del addon
docker-compose exec odoo odoo \ docker-compose exec odoo odoo \
@ -98,6 +99,8 @@ docker-compose exec odoo odoo \
--modules=addon_name \ --modules=addon_name \
--db=odoo \ --db=odoo \
--stop-after-init --stop-after-init
De alguna forma, exporta todas las cadenas de Odoo.
PEDIR AL USUARIO QUE GENERE EL POT DESDE LA UI DE ODOO
# Copiar el archivo generado # Copiar el archivo generado
docker-compose cp odoo:/tmp/addon_name.pot ./addon_name/i18n/ docker-compose cp odoo:/tmp/addon_name.pot ./addon_name/i18n/
@ -105,19 +108,7 @@ docker-compose cp odoo:/tmp/addon_name.pot ./addon_name/i18n/
### 2. Actualizar Archivos .po Existentes ### 2. Actualizar Archivos .po Existentes
```bash no usar msmerge, corrompe el po. Usa polib.
cd addon_name/i18n
# Actualizar español
msgmerge --update es.po addon_name.pot
# Actualizar euskera
msgmerge --update eu.po addon_name.pot
# Actualizar otros idiomas (si existen)
msgmerge --update ca.po addon_name.pot
msgmerge --update gl.po addon_name.pot
```
### 3. Traducir Términos Nuevos ### 3. Traducir Términos Nuevos

View file

@ -2,7 +2,8 @@
# Exclude migration scripts (post-migrate.py etc.) from mypy checks to avoid # Exclude migration scripts (post-migrate.py etc.) from mypy checks to avoid
# duplicate module name errors when multiple addons include scripts with the # duplicate module name errors when multiple addons include scripts with the
# same filename. # same filename.
exclude = .*/migrations/.* # Exclude scripts/ directory from mypy checks (utility scripts, not Odoo code)
exclude = .*/migrations/.*|^scripts/.*
# Ignore missing imports from Odoo modules # Ignore missing imports from Odoo modules
ignore_missing_imports = True ignore_missing_imports = True

View file

@ -0,0 +1,91 @@
#!/usr/bin/env python
"""Filter .po files to keep only entries for a specific module."""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Iterable
import polib
def _extract_modules_from_comment(entry: polib.POEntry) -> set[str]:
"""Return module names declared in translator comments (#.)."""
tcomment = entry.tcomment or ""
modules: set[str] = set()
for line in tcomment.splitlines():
stripped = line.strip()
lowered = stripped.lower()
if lowered.startswith("module:") or lowered.startswith("modules:"):
_, _, tail = stripped.partition(":")
for module in tail.split(","):
module_name = module.strip()
if module_name:
modules.add(module_name)
return modules
def belongs_to_module(entry: polib.POEntry, module_name: str) -> bool:
"""Return True if the entry references ONLY the target module."""
declared_modules = _extract_modules_from_comment(entry)
if declared_modules:
return declared_modules == {module_name}
locations = [occ[0] for occ in entry.occurrences if occ and occ[0]]
if locations:
return all(module_name in location for location in locations)
return False
def filter_po_file(path: Path, module_name: str, dry_run: bool = False) -> None:
po = polib.pofile(str(path))
kept_entries: list[polib.POEntry] = []
removed = 0
for entry in po:
if entry.msgid == "":
kept_entries.append(entry)
continue
if belongs_to_module(entry, module_name):
kept_entries.append(entry)
else:
removed += 1
if dry_run:
print(
f"[DRY-RUN] {path}: would keep {len(kept_entries)} entries, remove {removed} entries"
)
return
new_po = polib.POFile()
new_po.metadata = po.metadata
for entry in kept_entries:
new_po.append(entry)
new_po.save(str(path))
print(
f"Filtered {path}: kept {len(kept_entries)} entries, removed {removed} entries"
)
def parse_args(args: Iterable[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("module", help="Module technical name to keep")
parser.add_argument("files", nargs="+", type=Path, help=".po files to filter")
parser.add_argument("--dry-run", action="store_true", help="Only report counts")
return parser.parse_args(args)
def main() -> None:
args = parse_args()
for file_path in args.files:
filter_po_file(file_path, args.module, dry_run=args.dry_run)
if __name__ == "__main__":
main()

View file

@ -3,6 +3,7 @@ max-line-length = 88
max-complexity = 16 max-complexity = 16
select = C,E,F,W,B,B9 select = C,E,F,W,B,B9
ignore = E203,E501,W503,B950 ignore = E203,E501,W503,B950
exclude = scripts/
[isort] [isort]
profile = black profile = black

View file

@ -332,5 +332,5 @@ For issues, feature requests, or contributions:
**Version:** 18.0.1.3.1 **Version:** 18.0.1.3.1
**Odoo:** 18.0+ **Odoo:** 18.0+
**License:** AGPL-3 **License:** AGPL-3
**Maintainer:** Criptomart SL **Maintainer:** Criptomart
**Repository:** https://git.criptomart.net/KideKoop/kidekoop/odoo-addons **Repository:** https://git.criptomart.net/criptomart/addons-cm

View file

@ -3,17 +3,17 @@
<!-- License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) --> <!-- License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
<odoo> <odoo>
<data noupdate="0"> <data noupdate="0">
<!-- Ribbon: Out of Stock (Sin Stock) --> <!-- Ribbon: Out of Stock -->
<record id="out_of_stock_ribbon" model="product.ribbon"> <record id="out_of_stock_ribbon" model="product.ribbon">
<field name="name">Sin Stock</field> <field name="name">Out of Stock</field>
<field name="position">left</field> <field name="position">left</field>
<field name="text_color">#FFFFFF</field> <field name="text_color">#FFFFFF</field>
<field name="bg_color">#d9534f</field> <field name="bg_color">#d9534f</field>
</record> </record>
<!-- Ribbon: Low Stock (Pocas Existencias) --> <!-- Ribbon: Low Stock -->
<record id="low_stock_ribbon" model="product.ribbon"> <record id="low_stock_ribbon" model="product.ribbon">
<field name="name">Pocas Existencias</field> <field name="name">Low Stock</field>
<field name="position">left</field> <field name="position">left</field>
<field name="text_color">#FFFFFF</field> <field name="text_color">#FFFFFF</field>
<field name="bg_color">#ffc107</field> <field name="bg_color">#ffc107</field>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -138,6 +138,43 @@
// Use the global function from checkout_labels.js // Use the global function from checkout_labels.js
if (typeof window.renderCheckoutSummary === "function") { if (typeof window.renderCheckoutSummary === "function") {
window.renderCheckoutSummary(); window.renderCheckoutSummary();
// Vincular botón Entrega a Casa en carrito (shop)
var homeDeliveryBtn = document.getElementById("home-delivery-btn");
if (homeDeliveryBtn) {
homeDeliveryBtn.addEventListener("click", function () {
// Simular click en checkbox de checkout si existe
if (checkbox) {
checkbox.checked = !checkbox.checked;
var event = new Event("change", { bubbles: true });
checkbox.dispatchEvent(event);
} else {
// Alternar home delivery en localStorage/cart
// Aquí puedes implementar lógica para shop
console.log("[HomeDelivery] Toggle home delivery from cart header");
// TODO: Actualizar carrito y mostrar info
}
});
}
}
// Cargar borrador automáticamente al entrar en shop
var cartPage = document.querySelector(".eskaera-shop-page");
if (cartPage) {
var orderId = cartPage.getAttribute("data-order-id") || this.orderId;
var cartKey = "eskaera_" + orderId + "_cart";
var savedCart = localStorage.getItem(cartKey);
if (savedCart) {
try {
var cart = JSON.parse(savedCart);
var event = new CustomEvent("cartLoaded", { detail: { cart: cart } });
document.dispatchEvent(event);
console.log("[SHOP AUTO-LOAD] Cart loaded from localStorage");
} catch (e) {
console.error("[SHOP AUTO-LOAD] Error parsing cart:", e);
}
} else {
console.log("[SHOP AUTO-LOAD] No cart found in localStorage");
}
} }
}, 50); }, 50);
}, },

View file

@ -80,13 +80,13 @@
<group string="Delivery"> <group string="Delivery">
<field name="delivery_notice" placeholder="Information about home delivery..." nolabel="1"/> <field name="delivery_notice" placeholder="Information about home delivery..." nolabel="1"/>
</group> </group>
<group string="Catálogo de Productos"> <group string="Product Catalog">
<group string="Productos Incluidos" col="2"> <group string="Included Products" col="2">
<field name="supplier_ids" widget="many2many_tags" help="All products from these suppliers will be included"/> <field name="supplier_ids" widget="many2many_tags" help="All products from these suppliers will be included"/>
<field name="category_ids" widget="many2many_tags" help="All products in these categories (including subcategories) will be included"/> <field name="category_ids" widget="many2many_tags" help="All products in these categories (including subcategories) will be included"/>
<field name="product_ids" widget="many2many_tags" help="Specific products to include directly"/> <field name="product_ids" widget="many2many_tags" help="Specific products to include directly"/>
</group> </group>
<group string="Productos Excluidos" col="2"> <group string="Excluded Products" col="2">
<field name="excluded_supplier_ids" widget="many2many_tags" help="Suppliers excluded from this order. Products with these suppliers as main seller will not be available (blacklist has absolute priority)"/> <field name="excluded_supplier_ids" widget="many2many_tags" help="Suppliers excluded from this order. Products with these suppliers as main seller will not be available (blacklist has absolute priority)"/>
<field name="excluded_category_ids" widget="many2many_tags" help="Categories excluded from this order. Products in these categories and all their subcategories will not be available (blacklist has absolute priority)"/> <field name="excluded_category_ids" widget="many2many_tags" help="Categories excluded from this order. Products in these categories and all their subcategories will not be available (blacklist has absolute priority)"/>
<field name="excluded_product_ids" widget="many2many_tags" help="Products explicitly excluded from this order (blacklist has absolute priority over inclusions)"/> <field name="excluded_product_ids" widget="many2many_tags" help="Products explicitly excluded from this order (blacklist has absolute priority over inclusions)"/>

View file

@ -619,33 +619,26 @@
> >
<h6 class="mb-0 cart-title-sm" id="cart-title">My Cart</h6> <h6 class="mb-0 cart-title-sm" id="cart-title">My Cart</h6>
<div class="btn-group cart-btn-group gap-0" role="group"> <div class="btn-group cart-btn-group gap-0" role="group">
<button type="button" class="btn btn-primary cart-btn-compact" id="save-cart-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Save Cart" data-bs-toggle="tooltip"> <!-- Botón Entrega a Casa -->
<i class="fa fa-save cart-icon-size" /> <button type="button" class="btn btn-warning cart-btn-compact" id="home-delivery-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Home Delivery" data-bs-toggle="tooltip">
</button> <i class="fa fa-truck cart-icon-size" />
<button </button>
type="button" <button type="button" class="btn btn-primary cart-btn-compact" id="save-cart-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Save Cart" data-bs-toggle="tooltip">
class="btn btn-info cart-btn-compact" <i class="fa fa-save cart-icon-size" />
id="reload-cart-btn" </button>
t-attf-data-order-id="{{ group_order.id }}" <!-- Botón Entrega a Casa -->
data-bs-title="Reload Cart" <button type="button" class="btn btn-warning cart-btn-compact" id="home-delivery-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Home Delivery" data-bs-toggle="tooltip">
data-bs-toggle="tooltip" <i class="fa fa-truck cart-icon-size" />
> </button>
<i <a
class="fa fa-refresh cart-icon-size"
/>
</button>
<a
t-attf-href="/eskaera/{{ group_order.id }}/checkout" t-attf-href="/eskaera/{{ group_order.id }}/checkout"
class="btn btn-success cart-btn-compact" class="btn btn-success cart-btn-compact"
aria-label="Proceed to checkout" aria-label="Proceed to checkout"
data-bs-title="Proceed to Checkout" data-bs-title="Proceed to Checkout"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
> >
<i <i class="fa fa-check cart-icon-size" aria-hidden="true" />
class="fa fa-check cart-icon-size" </a>
aria-hidden="true"
/>
</a>
</div> </div>
</div> </div>
<div class="card-body cart-body-lg" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" aria-labelledby="cart-title" aria-live="polite" aria-relevant="additions removals"> <div class="card-body cart-body-lg" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" aria-labelledby="cart-title" aria-live="polite" aria-relevant="additions removals">
@ -751,117 +744,47 @@
</div> </div>
<!-- Order Info Card --> <!-- Order Info Card -->
<div <div class="order-info-card card border-0 shadow-sm mb-4">
class="order-info-card card border-0 shadow-sm mb-4"
>
<div class="card-body"> <div class="card-body">
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-4"> <div class="col-md-4">
<div class="info-item"> <div class="info-item">
<label <label t-att-class="'info-label'">Cutoff Day</label>
t-att-class="'info-label'" <t t-if="group_order.cutoff_day and group_order.cutoff_date">
>Cutoff Day</label> <span class="info-value">
<t <t t-esc="day_names[int(group_order.cutoff_day) % 7]" />
t-if="group_order.cutoff_day and group_order.cutoff_date" <span class="info-date">(<t t-esc="group_order.cutoff_date.strftime('%d/%m/%Y')" />)</span>
>
<span
class="info-value"
>
<t
t-esc="day_names[int(group_order.cutoff_day) % 7]"
/>
<span
class="info-date"
>(<t
t-esc="group_order.cutoff_date.strftime('%d/%m/%Y')"
/>)</span>
</span> </span>
</t> </t>
<t t-else="1"> <t t-else="1">
<span <span t-att-class="'text-muted small'">Not configured</span>
t-att-class="'text-muted small'"
>Not configured</span>
</t> </t>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="info-item"> <div class="info-item">
<t <t t-if="group_order.pickup_day and group_order.pickup_date">
t-if="group_order.pickup_day and group_order.pickup_date" <label t-att-class="'info-label'">Store Pickup Day</label>
> <span class="info-value" t-attf-data-pickup-date="{{ group_order.pickup_date }}" t-attf-data-delivery-date="{{ group_order.delivery_date }}">
<label <t t-esc="day_names[int(group_order.pickup_day) % 7]" />
t-att-class="'info-label'" <span class="info-date">(<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)</span>
>Store Pickup Day</label>
<span
class="info-value"
t-attf-data-pickup-date="{{ group_order.pickup_date }}"
t-attf-data-delivery-date="{{ group_order.delivery_date }}"
>
<t
t-esc="day_names[int(group_order.pickup_day) % 7]"
/>
<span
class="info-date"
>(<t
t-esc="group_order.pickup_date.strftime('%d/%m/%Y')"
/>)</span>
</span> </span>
</t> </t>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="info-item"> <div class="info-item">
<t <t t-if="group_order.delivery_date and group_order.home_delivery">
t-if="group_order.delivery_date and group_order.home_delivery" <label t-att-class="'info-label'">Home Delivery Day</label>
> <span class="info-value">
<label <t t-esc="day_names[group_order.delivery_date.weekday()]" />
t-att-class="'info-label'" <span class="info-date">(<t t-esc="group_order.delivery_date.strftime('%d/%m/%Y')" />)</span>
>Home Delivery Day</label>
<span
class="info-value"
>
<t
t-esc="day_names[group_order.delivery_date.weekday()]"
/>
<span
class="info-date"
>(<t
t-esc="group_order.delivery_date.strftime('%d/%m/%Y')"
/>)</span>
</span> </span>
</t> </t>
</div> </div>
</div> </div>
</div> </div>
<hr class="my-2" /> <hr class="my-2" />
<div class="row">
<div
class="col-md-6 text-muted small help-text-sm"
>
<i
class="fa fa-info-circle"
aria-hidden="true"
t-translation="off"
/>
<span
>Save your order as a draft before confirming to make final changes if needed.</span>
</div>
<div class="col-md-6 text-end">
<button
class="btn btn-outline-primary save-order-btn-styled"
id="save-order-btn"
t-attf-data-order-id="{{ group_order.id }}"
aria-label="Save order as draft"
>
<i
class="fa fa-save save-icon-size"
aria-hidden="true"
t-translation="off"
/>
<span>Save as Draft</span>
</button>
</div>
</div>
</div> </div>
</div> </div>
@ -943,25 +866,11 @@
t-translation="off" t-translation="off"
/> />
<span class="fw-bold"> <span class="fw-bold">
<t <t t-esc="_('Important')"/>
t-if="request.env.context.get('lang') == 'eu_ES' or request.env.context.get('lang') == 'eu'"
>Garrantzitsua</t>
<t
t-elif="request.env.context.get('lang') == 'es_ES' or request.env.context.get('lang') == 'es'"
>Importante</t>
<t t-else="">Important</t>
</span>: </span>:
</div> </div>
<p> <p>
<t <t t-esc="_('Once you confirm this order, you will not be able to modify it. Please review carefully before confirming.')"/>
t-if="request.env.context.get('lang') == 'eu_ES' or request.env.context.get('lang') == 'eu'"
>Behin eskaera hau berretsi ondoren, ezin izango duzu aldatu. Mesedez, arretaz berrikusi berretsi aurretik.</t>
<t
t-elif="request.env.context.get('lang') == 'es_ES' or request.env.context.get('lang') == 'es'"
>Una vez confirmes este pedido, no podrás modificarlo. Por favor, revisa cuidadosamente antes de confirmar.</t>
<t
t-else=""
>Once you confirm this order, you will not be able to modify it. Please review carefully before confirming.</t>
</p> </p>
<button <button
type="button" type="button"
@ -973,10 +882,10 @@
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="checkout-actions d-grid gap-3" id="checkout-form-labels"> <div class="checkout-actions d-grid gap-3" id="checkout-form-labels">
<button class="btn btn-success btn-lg" id="confirm-order-btn" t-attf-data-order-id="{{ group_order.id }}" data-confirmed-label="Order confirmed" data-pickup-label="Pickup Day" aria-label="Confirm and send order" data-bs-title="Confirm Order" data-bs-toggle="tooltip"> <button class="btn btn-success btn-lg" id="confirm-order-btn" t-attf-data-order-id="{{ group_order.id }}" data-confirmed-label="Order saved as draft" data-pickup-label="Pickup Day" aria-label="Save order as draft" data-bs-title="Save Draft" data-bs-toggle="tooltip">
<i class="fa fa-check-circle" aria-hidden="true" t-translation="off" /> <i class="fa fa-save" aria-hidden="true" t-translation="off" />
<span>Confirm Order</span> <span>Save Draft</span>
</button> </button>
<a t-attf-href="/eskaera/{{ group_order.id }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" data-bs-title="Back to Cart" data-bs-toggle="tooltip"> <a t-attf-href="/eskaera/{{ group_order.id }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" data-bs-title="Back to Cart" data-bs-toggle="tooltip">
<i class="fa fa-arrow-left" aria-hidden="true" t-translation="off" /> <i class="fa fa-arrow-left" aria-hidden="true" t-translation="off" />
<span>Back to Cart</span> <span>Back to Cart</span>