Jul 13 - Optimize code 2
This commit is contained in:
@@ -4,5 +4,6 @@ __pycache__/
|
|||||||
.env
|
.env
|
||||||
dev.db
|
dev.db
|
||||||
*.mo
|
*.mo
|
||||||
|
!app/translations/**/*.mo
|
||||||
instance/
|
instance/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Each category has subcategories and a JSON-driven `field_schema`
|
|||||||
| Reverse proxy / static | Nginx |
|
| Reverse proxy / static | Nginx |
|
||||||
| Process manager | systemd |
|
| Process manager | systemd |
|
||||||
| OS | Ubuntu 22.04 Server |
|
| OS | Ubuntu 22.04 Server |
|
||||||
| Background jobs | APScheduler or RQ (expiry sweep, email, image processing) |
|
| Background jobs | systemd timers (expiry/boost/keyword sweeps). RQ planned — not yet installed |
|
||||||
| Payments | Stripe (Billing + Payment Intents + Tax + Customer Portal) |
|
| Payments | Stripe (Billing + Payment Intents + Tax + Customer Portal) |
|
||||||
| Email | SMTP relay via Brevo (smart relay, existing infra) |
|
| Email | SMTP relay via Brevo (smart relay, existing infra) |
|
||||||
| i18n | Flask-Babel |
|
| i18n | Flask-Babel |
|
||||||
@@ -188,7 +188,8 @@ classifieds/
|
|||||||
│ │ │ # moderator_required
|
│ │ │ # moderator_required
|
||||||
│ │ ├── security.py # hash_password, verify_password, generate_token,
|
│ │ ├── security.py # hash_password, verify_password, generate_token,
|
||||||
│ │ │ # read_token (itsdangerous)
|
│ │ │ # read_token (itsdangerous)
|
||||||
│ │ └── text.py # normalize() — accent-insensitive (phở→pho, ñ→n)
|
│ │ ├── text.py # normalize() — accent-insensitive (phở→pho, ñ→n)
|
||||||
|
│ │ └── time.py # utcnow() — naive-UTC helper (replaces datetime.utcnow)
|
||||||
│ │
|
│ │
|
||||||
│ ├── templates/
|
│ ├── templates/
|
||||||
│ │ ├── base.html # Layout: responsive nav, OG/meta blocks, toast JS,
|
│ │ ├── base.html # Layout: responsive nav, OG/meta blocks, toast JS,
|
||||||
@@ -710,6 +711,9 @@ Phase 6: flag threshold → auto-flip, keyword blocklist, reports queue, audit_l
|
|||||||
- Contact masking for low-trust users
|
- Contact masking for low-trust users
|
||||||
- Signed time-limited tokens for email verify + password reset
|
- Signed time-limited tokens for email verify + password reset
|
||||||
- ProxyFix for correct `is_secure` + client-IP behind Nginx
|
- ProxyFix for correct `is_secure` + client-IP behind Nginx
|
||||||
|
- Mid-session ban enforcement: `enforce_active_account` before_request hook logs
|
||||||
|
out any authenticated user whose `status != active` on their next request
|
||||||
|
(Flask-Login only checks `is_active` at login time)
|
||||||
- Stripe webhook signature verification on every event
|
- Stripe webhook signature verification on every event
|
||||||
- Secrets via `.env` only — never committed
|
- Secrets via `.env` only — never committed
|
||||||
- HTTPS only in prod; `SESSION_COOKIE_SECURE=True` in ProdConfig
|
- HTTPS only in prod; `SESSION_COOKIE_SECURE=True` in ProdConfig
|
||||||
@@ -830,6 +834,8 @@ accounts. If `caching_sha2_password` errors: `pip install cryptography`.
|
|||||||
# 2. python3 -m venv venv && pip install -r requirements.txt
|
# 2. python3 -m venv venv && pip install -r requirements.txt
|
||||||
# 3. cp .env.example .env && nano .env (no inline comments!)
|
# 3. cp .env.example .env && nano .env (no inline comments!)
|
||||||
# 4. flask db upgrade && python seed.py
|
# 4. flask db upgrade && python seed.py
|
||||||
|
# 4b. Compile translations (committed .mo travel with the repo, but recompile
|
||||||
|
# after any .po change): pybabel compile -d app/translations
|
||||||
# 5. Copy systemd units:
|
# 5. Copy systemd units:
|
||||||
cp deploy/classifieds.service /etc/systemd/system/
|
cp deploy/classifieds.service /etc/systemd/system/
|
||||||
cp deploy/classifieds-expire.{service,timer} /etc/systemd/system/
|
cp deploy/classifieds-expire.{service,timer} /etc/systemd/system/
|
||||||
@@ -848,7 +854,9 @@ certbot --nginx -d classifieds.ngodanguyen.tech
|
|||||||
Nginx config independently of DNS
|
Nginx config independently of DNS
|
||||||
- DNS A record must point to server's public IP
|
- DNS A record must point to server's public IP
|
||||||
- `curl -4 ifconfig.me` vs `dig +short classifieds.ngodanguyen.tech` must match
|
- `curl -4 ifconfig.me` vs `dig +short classifieds.ngodanguyen.tech` must match
|
||||||
- CSS cache busting: copy to `style.v2.css` and update `base.html` link
|
- CSS cache busting: bump the `?v=N` query string on the `style.css` link in
|
||||||
|
`base.html` (single `style.css` file; the old `style.vN.css` copy approach was
|
||||||
|
dropped)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -861,6 +869,9 @@ certbot --nginx -d classifieds.ngodanguyen.tech
|
|||||||
- All datetimes UTC in DB. Localize only at render.
|
- All datetimes UTC in DB. Localize only at render.
|
||||||
- Money: integer cents everywhere. Never `float` for currency.
|
- Money: integer cents everywhere. Never `float` for currency.
|
||||||
- `db.session.get(Model, pk)` not `Model.query.get(pk)` (SA 2.0 deprecated).
|
- `db.session.get(Model, pk)` not `Model.query.get(pk)` (SA 2.0 deprecated).
|
||||||
|
- UTC now: use `utcnow()` from `app/utils/time.py` — never `datetime.utcnow()`
|
||||||
|
(deprecated on Python 3.12+). The helper returns a naive UTC datetime to match
|
||||||
|
the naive `DateTime` columns; do not mix in tz-aware datetimes.
|
||||||
- Enums: DB `ENUM` columns + Python `str, enum.Enum` in `models/enums.py`.
|
- Enums: DB `ENUM` columns + Python `str, enum.Enum` in `models/enums.py`.
|
||||||
- Validate `listings.attributes` against `field_schema` server-side on every save.
|
- Validate `listings.attributes` against `field_schema` server-side on every save.
|
||||||
- i18n: all user-facing strings in `_()`. No hardcoded English in templates.
|
- i18n: all user-facing strings in `_()`. No hardcoded English in templates.
|
||||||
@@ -1063,9 +1074,12 @@ certbot --nginx -d classifieds.ngodanguyen.tech
|
|||||||
- [ ] Listing preview before publish
|
- [ ] Listing preview before publish
|
||||||
|
|
||||||
**i18n completion**
|
**i18n completion**
|
||||||
- [ ] Extract all `_()` strings to `.pot`
|
- [x] Translate VI + ES `.po` files (75-string Phase 1–2 catalog: auth + listings chrome)
|
||||||
- [ ] Translate VI + ES `.po` files
|
- [x] Compile `.mo` for vi + es; verified loadable via gettext (placeholders + plural-forms intact)
|
||||||
- [ ] Compile `.mo`, test all three locales
|
- [x] `.mo` files un-gitignored so they deploy (see Known Issues)
|
||||||
|
- [ ] Re-extract `.pot` to capture Phase 3–7 strings (messaging/payments/admin) —
|
||||||
|
needs `pybabel extract` (Babel not currently installed); then translate + recompile
|
||||||
|
- [ ] Test all three locales end-to-end in the running app
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1115,11 +1129,17 @@ certbot --nginx -d classifieds.ngodanguyen.tech
|
|||||||
| `.env` inline comments | MUST NOT use `# comments` after values — python-dotenv does not strip them → `int()` ValueError on startup. |
|
| `.env` inline comments | MUST NOT use `# comments` after values — python-dotenv does not strip them → `int()` ValueError on startup. |
|
||||||
| Stripe webhook | `/billing/webhook` is `@csrf.exempt`. Always verify `Stripe-Signature` header first. |
|
| Stripe webhook | `/billing/webhook` is `@csrf.exempt`. Always verify `Stripe-Signature` header first. |
|
||||||
| MySQL user grants | Must create BOTH `@'localhost'` AND `@'127.0.0.1'` — MySQL treats them as different accounts. |
|
| MySQL user grants | Must create BOTH `@'localhost'` AND `@'127.0.0.1'` — MySQL treats them as different accounts. |
|
||||||
| CSS cache busting | Nginx serves static with 30d expires. To bust: copy to `style.vN.css` and update `base.html` link. |
|
| CSS cache busting | Nginx serves static with 30d expires. To bust: bump the `?v=N` query string on the `style.css` link in `base.html`. |
|
||||||
|
| Translations (.mo) | `*.mo` is gitignored **except** `app/translations/**` (negated) so compiled catalogs deploy with the code — the no-build-step deploy has no compile phase. Recompile with `pybabel compile -d app/translations` after editing any `.po`. |
|
||||||
|
| Python / datetime | Target 3.11–3.12 for prod parity. `datetime.utcnow()` is deprecated on 3.12+ — use `utcnow()` from `app/utils/time.py`. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_End of spec. Phase 1–6 complete, all smoke-test checks green.
|
_End of spec. Phase 1–6 complete. Phase 7 (Polish) in progress — SEO, UX, email
|
||||||
Phase 7 (Polish) in progress — SEO, UX, email notifications, and reviews done.
|
notifications, reviews, and VI/ES translation compilation done (existing catalog).
|
||||||
Remaining: metro landing pages, i18n .po compilation, Redis sessions, WebP thumbnails,
|
Remaining: metro landing pages, i18n re-extraction for Phase 3–7 strings, Redis
|
||||||
listing lightbox, load-more, "back to results", translation cache, weekly digest._
|
sessions, WebP thumbnails, listing lightbox, load-more, "back to results",
|
||||||
|
translation cache, weekly digest.
|
||||||
|
Note: run the smoke suite in a venv matching `requirements.txt` (Flask 3.0.3 /
|
||||||
|
Python 3.11–3.12); the ambient Python 3.14 env lacks `flask_login` and drifts from
|
||||||
|
the pins._
|
||||||
|
|||||||
Binary file not shown.
@@ -20,313 +20,313 @@ msgstr ""
|
|||||||
|
|
||||||
#: app/blueprints/auth/routes.py:28
|
#: app/blueprints/auth/routes.py:28
|
||||||
msgid "Verify your email"
|
msgid "Verify your email"
|
||||||
msgstr ""
|
msgstr "Verifica tu correo electrónico"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:29
|
#: app/blueprints/auth/routes.py:29
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Confirm your account: %(link)s"
|
msgid "Confirm your account: %(link)s"
|
||||||
msgstr ""
|
msgstr "Confirma tu cuenta: %(link)s"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:40
|
#: app/blueprints/auth/routes.py:40
|
||||||
msgid "Captcha verification failed."
|
msgid "Captcha verification failed."
|
||||||
msgstr ""
|
msgstr "La verificación de Captcha falló."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:45
|
#: app/blueprints/auth/routes.py:45
|
||||||
msgid "An account with that email already exists."
|
msgid "An account with that email already exists."
|
||||||
msgstr ""
|
msgstr "Ya existe una cuenta con ese correo electrónico."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:59
|
#: app/blueprints/auth/routes.py:59
|
||||||
msgid "Account created. Check your email to verify."
|
msgid "Account created. Check your email to verify."
|
||||||
msgstr ""
|
msgstr "Cuenta creada. Revisa tu correo para verificar."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:74
|
#: app/blueprints/auth/routes.py:74
|
||||||
msgid "Invalid email or password."
|
msgid "Invalid email or password."
|
||||||
msgstr ""
|
msgstr "Correo o contraseña no válidos."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:77
|
#: app/blueprints/auth/routes.py:77
|
||||||
msgid "This account is suspended."
|
msgid "This account is suspended."
|
||||||
msgstr ""
|
msgstr "Esta cuenta está suspendida."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:93
|
#: app/blueprints/auth/routes.py:93
|
||||||
msgid "Signed out."
|
msgid "Signed out."
|
||||||
msgstr ""
|
msgstr "Sesión cerrada."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:102
|
#: app/blueprints/auth/routes.py:102
|
||||||
msgid "Verification link is invalid or expired."
|
msgid "Verification link is invalid or expired."
|
||||||
msgstr ""
|
msgstr "El enlace de verificación no es válido o ha caducado."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:111
|
#: app/blueprints/auth/routes.py:111
|
||||||
msgid "Email verified. You're all set."
|
msgid "Email verified. You're all set."
|
||||||
msgstr ""
|
msgstr "Correo verificado. Todo listo."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:124
|
#: app/blueprints/auth/routes.py:124
|
||||||
msgid "Reset your password"
|
msgid "Reset your password"
|
||||||
msgstr ""
|
msgstr "Restablece tu contraseña"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:125
|
#: app/blueprints/auth/routes.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Reset link: %(link)s"
|
msgid "Reset link: %(link)s"
|
||||||
msgstr ""
|
msgstr "Enlace de restablecimiento: %(link)s"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:127
|
#: app/blueprints/auth/routes.py:127
|
||||||
msgid "If that email exists, a reset link has been sent."
|
msgid "If that email exists, a reset link has been sent."
|
||||||
msgstr ""
|
msgstr "Si ese correo existe, se ha enviado un enlace de restablecimiento."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:137
|
#: app/blueprints/auth/routes.py:137
|
||||||
msgid "Reset link is invalid or expired."
|
msgid "Reset link is invalid or expired."
|
||||||
msgstr ""
|
msgstr "El enlace de restablecimiento no es válido o ha caducado."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:146
|
#: app/blueprints/auth/routes.py:146
|
||||||
msgid "Password updated. Sign in."
|
msgid "Password updated. Sign in."
|
||||||
msgstr ""
|
msgstr "Contraseña actualizada. Inicia sesión."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:72
|
#: app/blueprints/listings/routes.py:72
|
||||||
msgid "ZIP not found; showing all results."
|
msgid "ZIP not found; showing all results."
|
||||||
msgstr ""
|
msgstr "Código postal no encontrado; mostrando todos los resultados."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:103
|
#: app/blueprints/listings/routes.py:103
|
||||||
msgid "You've reached your plan's active-listing limit."
|
msgid "You've reached your plan's active-listing limit."
|
||||||
msgstr ""
|
msgstr "Has alcanzado el límite de anuncios activos de tu plan."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:125 app/blueprints/listings/routes.py:171
|
#: app/blueprints/listings/routes.py:125 app/blueprints/listings/routes.py:171
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(f)s: %(m)s"
|
msgid "%(f)s: %(m)s"
|
||||||
msgstr ""
|
msgstr "%(f)s: %(m)s"
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:133
|
#: app/blueprints/listings/routes.py:133
|
||||||
msgid "Listing published."
|
msgid "Listing published."
|
||||||
msgstr ""
|
msgstr "Anuncio publicado."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:176
|
#: app/blueprints/listings/routes.py:176
|
||||||
msgid "Listing updated."
|
msgid "Listing updated."
|
||||||
msgstr ""
|
msgstr "Anuncio actualizado."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:194
|
#: app/blueprints/listings/routes.py:194
|
||||||
msgid "Listing deleted."
|
msgid "Listing deleted."
|
||||||
msgstr ""
|
msgstr "Anuncio eliminado."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:207
|
#: app/blueprints/listings/routes.py:207
|
||||||
msgid "Marked as sold."
|
msgid "Marked as sold."
|
||||||
msgstr ""
|
msgstr "Marcado como vendido."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:236
|
#: app/blueprints/listings/routes.py:236
|
||||||
msgid "Photo removed."
|
msgid "Photo removed."
|
||||||
msgstr ""
|
msgstr "Foto eliminada."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:258
|
#: app/blueprints/listings/routes.py:258
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Photo limit (%(n)s) reached for your plan."
|
msgid "Photo limit (%(n)s) reached for your plan."
|
||||||
msgstr ""
|
msgstr "Se alcanzó el límite de fotos (%(n)s) de tu plan."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:265
|
#: app/blueprints/listings/routes.py:265
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Image skipped: %(m)s"
|
msgid "Image skipped: %(m)s"
|
||||||
msgstr ""
|
msgstr "Imagen omitida: %(m)s"
|
||||||
|
|
||||||
#: app/templates/base.html:15
|
#: app/templates/base.html:15
|
||||||
msgid "Browse"
|
msgid "Browse"
|
||||||
msgstr ""
|
msgstr "Explorar"
|
||||||
|
|
||||||
#: app/templates/base.html:17
|
#: app/templates/base.html:17
|
||||||
msgid "Post"
|
msgid "Post"
|
||||||
msgstr ""
|
msgstr "Publicar"
|
||||||
|
|
||||||
#: app/templates/base.html:18 app/templates/listings/mine.html:2
|
#: app/templates/base.html:18 app/templates/listings/mine.html:2
|
||||||
#: app/templates/listings/mine.html:6
|
#: app/templates/listings/mine.html:6
|
||||||
msgid "My listings"
|
msgid "My listings"
|
||||||
msgstr ""
|
msgstr "Mis anuncios"
|
||||||
|
|
||||||
#: app/templates/base.html:20
|
#: app/templates/base.html:20
|
||||||
msgid "Sign out"
|
msgid "Sign out"
|
||||||
msgstr ""
|
msgstr "Cerrar sesión"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:3 app/templates/auth/login.html:6
|
#: app/templates/auth/login.html:3 app/templates/auth/login.html:6
|
||||||
#: app/templates/base.html:22
|
#: app/templates/base.html:22
|
||||||
msgid "Sign in"
|
msgid "Sign in"
|
||||||
msgstr ""
|
msgstr "Iniciar sesión"
|
||||||
|
|
||||||
#: app/templates/auth/register.html:3 app/templates/base.html:23
|
#: app/templates/auth/register.html:3 app/templates/base.html:23
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr "Registrarse"
|
||||||
|
|
||||||
#: app/templates/index.html:2
|
#: app/templates/index.html:2
|
||||||
msgid "Classifieds — Home"
|
msgid "Classifieds — Home"
|
||||||
msgstr ""
|
msgstr "Clasificados — Inicio"
|
||||||
|
|
||||||
#: app/templates/index.html:5
|
#: app/templates/index.html:5
|
||||||
msgid "Find what you need. Post what you offer."
|
msgid "Find what you need. Post what you offer."
|
||||||
msgstr ""
|
msgstr "Encuentra lo que necesitas. Publica lo que ofreces."
|
||||||
|
|
||||||
#: app/templates/index.html:6
|
#: app/templates/index.html:6
|
||||||
msgid "Buy, sell, request, hire, and connect across your community."
|
msgid "Buy, sell, request, hire, and connect across your community."
|
||||||
msgstr ""
|
msgstr "Compra, vende, solicita, contrata y conecta en tu comunidad."
|
||||||
|
|
||||||
#: app/templates/index.html:7 app/templates/listings/browse.html:2
|
#: app/templates/index.html:7 app/templates/listings/browse.html:2
|
||||||
msgid "Browse listings"
|
msgid "Browse listings"
|
||||||
msgstr ""
|
msgstr "Explorar anuncios"
|
||||||
|
|
||||||
#: app/templates/index.html:9
|
#: app/templates/index.html:9
|
||||||
msgid "Get started"
|
msgid "Get started"
|
||||||
msgstr ""
|
msgstr "Comenzar"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:15
|
#: app/templates/auth/login.html:15
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr ""
|
msgstr "¿Olvidaste tu contraseña?"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:16 app/templates/auth/register.html:6
|
#: app/templates/auth/login.html:16 app/templates/auth/register.html:6
|
||||||
msgid "Create account"
|
msgid "Create account"
|
||||||
msgstr ""
|
msgstr "Crear cuenta"
|
||||||
|
|
||||||
#: app/templates/auth/register.html:16
|
#: app/templates/auth/register.html:16
|
||||||
msgid "Already have an account? Sign in"
|
msgid "Already have an account? Sign in"
|
||||||
msgstr ""
|
msgstr "¿Ya tienes una cuenta? Inicia sesión"
|
||||||
|
|
||||||
#: app/templates/auth/reset.html:3 app/templates/auth/reset.html:6
|
#: app/templates/auth/reset.html:3 app/templates/auth/reset.html:6
|
||||||
msgid "Set new password"
|
msgid "Set new password"
|
||||||
msgstr ""
|
msgstr "Establecer nueva contraseña"
|
||||||
|
|
||||||
#: app/templates/auth/reset_request.html:3
|
#: app/templates/auth/reset_request.html:3
|
||||||
#: app/templates/auth/reset_request.html:6
|
#: app/templates/auth/reset_request.html:6
|
||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr ""
|
msgstr "Restablecer contraseña"
|
||||||
|
|
||||||
#: app/templates/errors/403.html:3
|
#: app/templates/errors/403.html:3
|
||||||
msgid "Forbidden — you do not have access."
|
msgid "Forbidden — you do not have access."
|
||||||
msgstr ""
|
msgstr "Prohibido: no tienes acceso."
|
||||||
|
|
||||||
#: app/templates/errors/403.html:3 app/templates/errors/404.html:3
|
#: app/templates/errors/403.html:3 app/templates/errors/404.html:3
|
||||||
#: app/templates/errors/500.html:3
|
#: app/templates/errors/500.html:3
|
||||||
msgid "Back home"
|
msgid "Back home"
|
||||||
msgstr ""
|
msgstr "Volver al inicio"
|
||||||
|
|
||||||
#: app/templates/errors/404.html:3
|
#: app/templates/errors/404.html:3
|
||||||
msgid "Not found."
|
msgid "Not found."
|
||||||
msgstr ""
|
msgstr "No encontrado."
|
||||||
|
|
||||||
#: app/templates/errors/500.html:3
|
#: app/templates/errors/500.html:3
|
||||||
msgid "Something went wrong."
|
msgid "Something went wrong."
|
||||||
msgstr ""
|
msgstr "Algo salió mal."
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:6
|
#: app/templates/listings/browse.html:6
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr "Filtrar"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:9
|
#: app/templates/listings/browse.html:9
|
||||||
msgid "Keyword"
|
msgid "Keyword"
|
||||||
msgstr ""
|
msgstr "Palabra clave"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:13
|
#: app/templates/listings/browse.html:13
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr ""
|
msgstr "Categoría"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:15
|
#: app/templates/listings/browse.html:15
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr "Todos"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:21
|
#: app/templates/listings/browse.html:21
|
||||||
msgid "State"
|
msgid "State"
|
||||||
msgstr ""
|
msgstr "Estado"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:24
|
#: app/templates/listings/browse.html:24
|
||||||
msgid "Min $"
|
msgid "Min $"
|
||||||
msgstr ""
|
msgstr "Mín $"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:26
|
#: app/templates/listings/browse.html:26
|
||||||
msgid "Max $"
|
msgid "Max $"
|
||||||
msgstr ""
|
msgstr "Máx $"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:30
|
#: app/templates/listings/browse.html:30
|
||||||
msgid "Near ZIP"
|
msgid "Near ZIP"
|
||||||
msgstr ""
|
msgstr "Cerca del código postal"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:32
|
#: app/templates/listings/browse.html:32
|
||||||
msgid "Radius (mi)"
|
msgid "Radius (mi)"
|
||||||
msgstr ""
|
msgstr "Radio (mi)"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:35
|
#: app/templates/listings/browse.html:35
|
||||||
msgid "Apply"
|
msgid "Apply"
|
||||||
msgstr ""
|
msgstr "Aplicar"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:40
|
#: app/templates/listings/browse.html:40
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(n)s results within %(r)s mi of %(z)s"
|
msgid "%(n)s results within %(r)s mi of %(z)s"
|
||||||
msgstr ""
|
msgstr "%(n)s resultados dentro de %(r)s mi de %(z)s"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:41
|
#: app/templates/listings/browse.html:41
|
||||||
msgid "No listings found."
|
msgid "No listings found."
|
||||||
msgstr ""
|
msgstr "No se encontraron anuncios."
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:46
|
#: app/templates/listings/browse.html:46
|
||||||
msgid "No photo"
|
msgid "No photo"
|
||||||
msgstr ""
|
msgstr "Sin foto"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:51
|
#: app/templates/listings/browse.html:51
|
||||||
msgid "Featured"
|
msgid "Featured"
|
||||||
msgstr ""
|
msgstr "Destacado"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:63
|
#: app/templates/listings/browse.html:63
|
||||||
msgid "Prev"
|
msgid "Prev"
|
||||||
msgstr ""
|
msgstr "Anterior"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:64
|
#: app/templates/listings/browse.html:64
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr ""
|
msgstr "Siguiente"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:14
|
#: app/templates/listings/detail.html:14
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(n)s views"
|
msgid "%(n)s views"
|
||||||
msgstr ""
|
msgstr "%(n)s vistas"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:41
|
#: app/templates/listings/detail.html:41
|
||||||
msgid "Verified"
|
msgid "Verified"
|
||||||
msgstr ""
|
msgstr "Verificado"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:44 app/templates/listings/mine.html:19
|
#: app/templates/listings/detail.html:44 app/templates/listings/mine.html:19
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr "Editar"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:47
|
#: app/templates/listings/detail.html:47
|
||||||
msgid "Mark sold"
|
msgid "Mark sold"
|
||||||
msgstr ""
|
msgstr "Marcar vendido"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:51
|
#: app/templates/listings/detail.html:51
|
||||||
msgid "Delete this listing?"
|
msgid "Delete this listing?"
|
||||||
msgstr ""
|
msgstr "¿Eliminar este anuncio?"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:52
|
#: app/templates/listings/detail.html:52
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "Eliminar"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:55
|
#: app/templates/listings/detail.html:55
|
||||||
msgid "Messaging arrives in Phase 3."
|
msgid "Messaging arrives in Phase 3."
|
||||||
msgstr ""
|
msgstr "La mensajería llega en la Fase 3."
|
||||||
|
|
||||||
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
||||||
msgid "Edit listing"
|
msgid "Edit listing"
|
||||||
msgstr ""
|
msgstr "Editar anuncio"
|
||||||
|
|
||||||
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
||||||
msgid "New listing"
|
msgid "New listing"
|
||||||
msgstr ""
|
msgstr "Nuevo anuncio"
|
||||||
|
|
||||||
#: app/templates/listings/form.html:47
|
#: app/templates/listings/form.html:47
|
||||||
msgid "Current photos"
|
msgid "Current photos"
|
||||||
msgstr ""
|
msgstr "Fotos actuales"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:7
|
#: app/templates/listings/mine.html:7
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Active: %(a)s"
|
msgid "Active: %(a)s"
|
||||||
msgstr ""
|
msgstr "Activos: %(a)s"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:8
|
#: app/templates/listings/mine.html:8
|
||||||
msgid "Post new"
|
msgid "Post new"
|
||||||
msgstr ""
|
msgstr "Publicar nuevo"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:10
|
#: app/templates/listings/mine.html:10
|
||||||
msgid "No listings yet."
|
msgid "No listings yet."
|
||||||
msgstr ""
|
msgstr "Aún no hay anuncios."
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:18
|
#: app/templates/listings/mine.html:18
|
||||||
msgid "views"
|
msgid "views"
|
||||||
msgstr ""
|
msgstr "vistas"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -20,313 +20,313 @@ msgstr ""
|
|||||||
|
|
||||||
#: app/blueprints/auth/routes.py:28
|
#: app/blueprints/auth/routes.py:28
|
||||||
msgid "Verify your email"
|
msgid "Verify your email"
|
||||||
msgstr ""
|
msgstr "Xác minh email của bạn"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:29
|
#: app/blueprints/auth/routes.py:29
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Confirm your account: %(link)s"
|
msgid "Confirm your account: %(link)s"
|
||||||
msgstr ""
|
msgstr "Xác nhận tài khoản của bạn: %(link)s"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:40
|
#: app/blueprints/auth/routes.py:40
|
||||||
msgid "Captcha verification failed."
|
msgid "Captcha verification failed."
|
||||||
msgstr ""
|
msgstr "Xác minh Captcha thất bại."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:45
|
#: app/blueprints/auth/routes.py:45
|
||||||
msgid "An account with that email already exists."
|
msgid "An account with that email already exists."
|
||||||
msgstr ""
|
msgstr "Đã tồn tại tài khoản với email đó."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:59
|
#: app/blueprints/auth/routes.py:59
|
||||||
msgid "Account created. Check your email to verify."
|
msgid "Account created. Check your email to verify."
|
||||||
msgstr ""
|
msgstr "Đã tạo tài khoản. Kiểm tra email để xác minh."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:74
|
#: app/blueprints/auth/routes.py:74
|
||||||
msgid "Invalid email or password."
|
msgid "Invalid email or password."
|
||||||
msgstr ""
|
msgstr "Email hoặc mật khẩu không hợp lệ."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:77
|
#: app/blueprints/auth/routes.py:77
|
||||||
msgid "This account is suspended."
|
msgid "This account is suspended."
|
||||||
msgstr ""
|
msgstr "Tài khoản này đã bị tạm khóa."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:93
|
#: app/blueprints/auth/routes.py:93
|
||||||
msgid "Signed out."
|
msgid "Signed out."
|
||||||
msgstr ""
|
msgstr "Đã đăng xuất."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:102
|
#: app/blueprints/auth/routes.py:102
|
||||||
msgid "Verification link is invalid or expired."
|
msgid "Verification link is invalid or expired."
|
||||||
msgstr ""
|
msgstr "Liên kết xác minh không hợp lệ hoặc đã hết hạn."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:111
|
#: app/blueprints/auth/routes.py:111
|
||||||
msgid "Email verified. You're all set."
|
msgid "Email verified. You're all set."
|
||||||
msgstr ""
|
msgstr "Đã xác minh email. Bạn đã sẵn sàng."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:124
|
#: app/blueprints/auth/routes.py:124
|
||||||
msgid "Reset your password"
|
msgid "Reset your password"
|
||||||
msgstr ""
|
msgstr "Đặt lại mật khẩu của bạn"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:125
|
#: app/blueprints/auth/routes.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Reset link: %(link)s"
|
msgid "Reset link: %(link)s"
|
||||||
msgstr ""
|
msgstr "Liên kết đặt lại: %(link)s"
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:127
|
#: app/blueprints/auth/routes.py:127
|
||||||
msgid "If that email exists, a reset link has been sent."
|
msgid "If that email exists, a reset link has been sent."
|
||||||
msgstr ""
|
msgstr "Nếu email đó tồn tại, liên kết đặt lại đã được gửi."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:137
|
#: app/blueprints/auth/routes.py:137
|
||||||
msgid "Reset link is invalid or expired."
|
msgid "Reset link is invalid or expired."
|
||||||
msgstr ""
|
msgstr "Liên kết đặt lại không hợp lệ hoặc đã hết hạn."
|
||||||
|
|
||||||
#: app/blueprints/auth/routes.py:146
|
#: app/blueprints/auth/routes.py:146
|
||||||
msgid "Password updated. Sign in."
|
msgid "Password updated. Sign in."
|
||||||
msgstr ""
|
msgstr "Đã cập nhật mật khẩu. Đăng nhập."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:72
|
#: app/blueprints/listings/routes.py:72
|
||||||
msgid "ZIP not found; showing all results."
|
msgid "ZIP not found; showing all results."
|
||||||
msgstr ""
|
msgstr "Không tìm thấy mã ZIP; hiển thị tất cả kết quả."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:103
|
#: app/blueprints/listings/routes.py:103
|
||||||
msgid "You've reached your plan's active-listing limit."
|
msgid "You've reached your plan's active-listing limit."
|
||||||
msgstr ""
|
msgstr "Bạn đã đạt đến giới hạn tin đang đăng của gói."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:125 app/blueprints/listings/routes.py:171
|
#: app/blueprints/listings/routes.py:125 app/blueprints/listings/routes.py:171
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(f)s: %(m)s"
|
msgid "%(f)s: %(m)s"
|
||||||
msgstr ""
|
msgstr "%(f)s: %(m)s"
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:133
|
#: app/blueprints/listings/routes.py:133
|
||||||
msgid "Listing published."
|
msgid "Listing published."
|
||||||
msgstr ""
|
msgstr "Đã đăng tin."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:176
|
#: app/blueprints/listings/routes.py:176
|
||||||
msgid "Listing updated."
|
msgid "Listing updated."
|
||||||
msgstr ""
|
msgstr "Đã cập nhật tin."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:194
|
#: app/blueprints/listings/routes.py:194
|
||||||
msgid "Listing deleted."
|
msgid "Listing deleted."
|
||||||
msgstr ""
|
msgstr "Đã xóa tin."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:207
|
#: app/blueprints/listings/routes.py:207
|
||||||
msgid "Marked as sold."
|
msgid "Marked as sold."
|
||||||
msgstr ""
|
msgstr "Đã đánh dấu là đã bán."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:236
|
#: app/blueprints/listings/routes.py:236
|
||||||
msgid "Photo removed."
|
msgid "Photo removed."
|
||||||
msgstr ""
|
msgstr "Đã xóa ảnh."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:258
|
#: app/blueprints/listings/routes.py:258
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Photo limit (%(n)s) reached for your plan."
|
msgid "Photo limit (%(n)s) reached for your plan."
|
||||||
msgstr ""
|
msgstr "Đã đạt giới hạn ảnh (%(n)s) của gói."
|
||||||
|
|
||||||
#: app/blueprints/listings/routes.py:265
|
#: app/blueprints/listings/routes.py:265
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Image skipped: %(m)s"
|
msgid "Image skipped: %(m)s"
|
||||||
msgstr ""
|
msgstr "Đã bỏ qua ảnh: %(m)s"
|
||||||
|
|
||||||
#: app/templates/base.html:15
|
#: app/templates/base.html:15
|
||||||
msgid "Browse"
|
msgid "Browse"
|
||||||
msgstr ""
|
msgstr "Duyệt"
|
||||||
|
|
||||||
#: app/templates/base.html:17
|
#: app/templates/base.html:17
|
||||||
msgid "Post"
|
msgid "Post"
|
||||||
msgstr ""
|
msgstr "Đăng tin"
|
||||||
|
|
||||||
#: app/templates/base.html:18 app/templates/listings/mine.html:2
|
#: app/templates/base.html:18 app/templates/listings/mine.html:2
|
||||||
#: app/templates/listings/mine.html:6
|
#: app/templates/listings/mine.html:6
|
||||||
msgid "My listings"
|
msgid "My listings"
|
||||||
msgstr ""
|
msgstr "Tin của tôi"
|
||||||
|
|
||||||
#: app/templates/base.html:20
|
#: app/templates/base.html:20
|
||||||
msgid "Sign out"
|
msgid "Sign out"
|
||||||
msgstr ""
|
msgstr "Đăng xuất"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:3 app/templates/auth/login.html:6
|
#: app/templates/auth/login.html:3 app/templates/auth/login.html:6
|
||||||
#: app/templates/base.html:22
|
#: app/templates/base.html:22
|
||||||
msgid "Sign in"
|
msgid "Sign in"
|
||||||
msgstr ""
|
msgstr "Đăng nhập"
|
||||||
|
|
||||||
#: app/templates/auth/register.html:3 app/templates/base.html:23
|
#: app/templates/auth/register.html:3 app/templates/base.html:23
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr "Đăng ký"
|
||||||
|
|
||||||
#: app/templates/index.html:2
|
#: app/templates/index.html:2
|
||||||
msgid "Classifieds — Home"
|
msgid "Classifieds — Home"
|
||||||
msgstr ""
|
msgstr "Rao vặt — Trang chủ"
|
||||||
|
|
||||||
#: app/templates/index.html:5
|
#: app/templates/index.html:5
|
||||||
msgid "Find what you need. Post what you offer."
|
msgid "Find what you need. Post what you offer."
|
||||||
msgstr ""
|
msgstr "Tìm thứ bạn cần. Đăng thứ bạn có."
|
||||||
|
|
||||||
#: app/templates/index.html:6
|
#: app/templates/index.html:6
|
||||||
msgid "Buy, sell, request, hire, and connect across your community."
|
msgid "Buy, sell, request, hire, and connect across your community."
|
||||||
msgstr ""
|
msgstr "Mua, bán, yêu cầu, tuyển dụng và kết nối trong cộng đồng của bạn."
|
||||||
|
|
||||||
#: app/templates/index.html:7 app/templates/listings/browse.html:2
|
#: app/templates/index.html:7 app/templates/listings/browse.html:2
|
||||||
msgid "Browse listings"
|
msgid "Browse listings"
|
||||||
msgstr ""
|
msgstr "Duyệt tin đăng"
|
||||||
|
|
||||||
#: app/templates/index.html:9
|
#: app/templates/index.html:9
|
||||||
msgid "Get started"
|
msgid "Get started"
|
||||||
msgstr ""
|
msgstr "Bắt đầu"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:15
|
#: app/templates/auth/login.html:15
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr ""
|
msgstr "Quên mật khẩu?"
|
||||||
|
|
||||||
#: app/templates/auth/login.html:16 app/templates/auth/register.html:6
|
#: app/templates/auth/login.html:16 app/templates/auth/register.html:6
|
||||||
msgid "Create account"
|
msgid "Create account"
|
||||||
msgstr ""
|
msgstr "Tạo tài khoản"
|
||||||
|
|
||||||
#: app/templates/auth/register.html:16
|
#: app/templates/auth/register.html:16
|
||||||
msgid "Already have an account? Sign in"
|
msgid "Already have an account? Sign in"
|
||||||
msgstr ""
|
msgstr "Đã có tài khoản? Đăng nhập"
|
||||||
|
|
||||||
#: app/templates/auth/reset.html:3 app/templates/auth/reset.html:6
|
#: app/templates/auth/reset.html:3 app/templates/auth/reset.html:6
|
||||||
msgid "Set new password"
|
msgid "Set new password"
|
||||||
msgstr ""
|
msgstr "Đặt mật khẩu mới"
|
||||||
|
|
||||||
#: app/templates/auth/reset_request.html:3
|
#: app/templates/auth/reset_request.html:3
|
||||||
#: app/templates/auth/reset_request.html:6
|
#: app/templates/auth/reset_request.html:6
|
||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr ""
|
msgstr "Đặt lại mật khẩu"
|
||||||
|
|
||||||
#: app/templates/errors/403.html:3
|
#: app/templates/errors/403.html:3
|
||||||
msgid "Forbidden — you do not have access."
|
msgid "Forbidden — you do not have access."
|
||||||
msgstr ""
|
msgstr "Bị cấm — bạn không có quyền truy cập."
|
||||||
|
|
||||||
#: app/templates/errors/403.html:3 app/templates/errors/404.html:3
|
#: app/templates/errors/403.html:3 app/templates/errors/404.html:3
|
||||||
#: app/templates/errors/500.html:3
|
#: app/templates/errors/500.html:3
|
||||||
msgid "Back home"
|
msgid "Back home"
|
||||||
msgstr ""
|
msgstr "Về trang chủ"
|
||||||
|
|
||||||
#: app/templates/errors/404.html:3
|
#: app/templates/errors/404.html:3
|
||||||
msgid "Not found."
|
msgid "Not found."
|
||||||
msgstr ""
|
msgstr "Không tìm thấy."
|
||||||
|
|
||||||
#: app/templates/errors/500.html:3
|
#: app/templates/errors/500.html:3
|
||||||
msgid "Something went wrong."
|
msgid "Something went wrong."
|
||||||
msgstr ""
|
msgstr "Đã xảy ra lỗi."
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:6
|
#: app/templates/listings/browse.html:6
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr "Lọc"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:9
|
#: app/templates/listings/browse.html:9
|
||||||
msgid "Keyword"
|
msgid "Keyword"
|
||||||
msgstr ""
|
msgstr "Từ khóa"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:13
|
#: app/templates/listings/browse.html:13
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr ""
|
msgstr "Danh mục"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:15
|
#: app/templates/listings/browse.html:15
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr "Tất cả"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:21
|
#: app/templates/listings/browse.html:21
|
||||||
msgid "State"
|
msgid "State"
|
||||||
msgstr ""
|
msgstr "Tiểu bang"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:24
|
#: app/templates/listings/browse.html:24
|
||||||
msgid "Min $"
|
msgid "Min $"
|
||||||
msgstr ""
|
msgstr "Giá tối thiểu $"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:26
|
#: app/templates/listings/browse.html:26
|
||||||
msgid "Max $"
|
msgid "Max $"
|
||||||
msgstr ""
|
msgstr "Giá tối đa $"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:30
|
#: app/templates/listings/browse.html:30
|
||||||
msgid "Near ZIP"
|
msgid "Near ZIP"
|
||||||
msgstr ""
|
msgstr "Gần mã ZIP"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:32
|
#: app/templates/listings/browse.html:32
|
||||||
msgid "Radius (mi)"
|
msgid "Radius (mi)"
|
||||||
msgstr ""
|
msgstr "Bán kính (dặm)"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:35
|
#: app/templates/listings/browse.html:35
|
||||||
msgid "Apply"
|
msgid "Apply"
|
||||||
msgstr ""
|
msgstr "Áp dụng"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:40
|
#: app/templates/listings/browse.html:40
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(n)s results within %(r)s mi of %(z)s"
|
msgid "%(n)s results within %(r)s mi of %(z)s"
|
||||||
msgstr ""
|
msgstr "%(n)s kết quả trong vòng %(r)s dặm quanh %(z)s"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:41
|
#: app/templates/listings/browse.html:41
|
||||||
msgid "No listings found."
|
msgid "No listings found."
|
||||||
msgstr ""
|
msgstr "Không tìm thấy tin đăng."
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:46
|
#: app/templates/listings/browse.html:46
|
||||||
msgid "No photo"
|
msgid "No photo"
|
||||||
msgstr ""
|
msgstr "Không có ảnh"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:51
|
#: app/templates/listings/browse.html:51
|
||||||
msgid "Featured"
|
msgid "Featured"
|
||||||
msgstr ""
|
msgstr "Nổi bật"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:63
|
#: app/templates/listings/browse.html:63
|
||||||
msgid "Prev"
|
msgid "Prev"
|
||||||
msgstr ""
|
msgstr "Trước"
|
||||||
|
|
||||||
#: app/templates/listings/browse.html:64
|
#: app/templates/listings/browse.html:64
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr ""
|
msgstr "Sau"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:14
|
#: app/templates/listings/detail.html:14
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(n)s views"
|
msgid "%(n)s views"
|
||||||
msgstr ""
|
msgstr "%(n)s lượt xem"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:41
|
#: app/templates/listings/detail.html:41
|
||||||
msgid "Verified"
|
msgid "Verified"
|
||||||
msgstr ""
|
msgstr "Đã xác minh"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:44 app/templates/listings/mine.html:19
|
#: app/templates/listings/detail.html:44 app/templates/listings/mine.html:19
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr "Sửa"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:47
|
#: app/templates/listings/detail.html:47
|
||||||
msgid "Mark sold"
|
msgid "Mark sold"
|
||||||
msgstr ""
|
msgstr "Đánh dấu đã bán"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:51
|
#: app/templates/listings/detail.html:51
|
||||||
msgid "Delete this listing?"
|
msgid "Delete this listing?"
|
||||||
msgstr ""
|
msgstr "Xóa tin này?"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:52
|
#: app/templates/listings/detail.html:52
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "Xóa"
|
||||||
|
|
||||||
#: app/templates/listings/detail.html:55
|
#: app/templates/listings/detail.html:55
|
||||||
msgid "Messaging arrives in Phase 3."
|
msgid "Messaging arrives in Phase 3."
|
||||||
msgstr ""
|
msgstr "Tính năng nhắn tin sẽ có ở Giai đoạn 3."
|
||||||
|
|
||||||
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
||||||
msgid "Edit listing"
|
msgid "Edit listing"
|
||||||
msgstr ""
|
msgstr "Sửa tin"
|
||||||
|
|
||||||
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
#: app/templates/listings/form.html:3 app/templates/listings/form.html:6
|
||||||
msgid "New listing"
|
msgid "New listing"
|
||||||
msgstr ""
|
msgstr "Tin mới"
|
||||||
|
|
||||||
#: app/templates/listings/form.html:47
|
#: app/templates/listings/form.html:47
|
||||||
msgid "Current photos"
|
msgid "Current photos"
|
||||||
msgstr ""
|
msgstr "Ảnh hiện tại"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:7
|
#: app/templates/listings/mine.html:7
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Active: %(a)s"
|
msgid "Active: %(a)s"
|
||||||
msgstr ""
|
msgstr "Đang đăng: %(a)s"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:8
|
#: app/templates/listings/mine.html:8
|
||||||
msgid "Post new"
|
msgid "Post new"
|
||||||
msgstr ""
|
msgstr "Đăng tin mới"
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:10
|
#: app/templates/listings/mine.html:10
|
||||||
msgid "No listings yet."
|
msgid "No listings yet."
|
||||||
msgstr ""
|
msgstr "Chưa có tin đăng."
|
||||||
|
|
||||||
#: app/templates/listings/mine.html:18
|
#: app/templates/listings/mine.html:18
|
||||||
msgid "views"
|
msgid "views"
|
||||||
msgstr ""
|
msgstr "lượt xem"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user