feat: adicionei inglês.

This commit is contained in:
Ricardo Carneiro 2026-03-08 19:39:33 -03:00
parent 311efcae62
commit 6301e33686
26 changed files with 3998 additions and 940 deletions

View File

@ -0,0 +1,129 @@
---
title: "PNG, WebP or SVG? Choosing the Right Format for Your QR Code"
description: "Technical comparison between PNG, WebP and SVG for QR codes via API. Learn which format to use depending on your use case: email, printing, web or apps."
keywords: "qr code png, qr code webp, qr code svg, qr code image format, api qr code format"
author: "QRRapido"
date: 2026-03-08
lastmod: 2026-03-08
image: ""
---
# PNG, WebP or SVG? Choosing the Right Format for Your QR Code
The QRRapido API supports three output formats: `png`, `webp` and `svg`. Each has distinct characteristics that impact file size, quality and compatibility. Use the `outputFormat` parameter in the request to choose.
```json
{
"content": "https://yoursite.com",
"type": "url",
"outputFormat": "webp"
}
```
---
## Quick Summary
| Format | Size | Lossless scaling | Compatibility | Ideal for |
|---|---|---|---|---|
| **PNG** | Medium | No (raster) | Universal | Printing, email, legacy |
| **WebP** | Small (~30% smaller) | No (raster) | Modern browsers | Web, apps, APIs |
| **SVG** | Variable (usually smaller) | **Yes** | Browsers, Adobe, Figma | Logos, banners, vector printing |
---
## PNG — The Universal Standard
PNG is a lossless raster format, ideal for QR codes because it preserves 100% of the black and white pixels without introducing artifacts.
**When to use:**
- Sending by email (legacy email clients don't support WebP)
- Integration with legacy systems
- When maximum compatibility is a priority
**Why not JPEG?**
> JPEG uses lossy compression that introduces blur artifacts on the edges of QR code modules. This can make the code unreadable, especially at smaller sizes. **Never use JPEG for QR codes.**
```json
{ "outputFormat": "png" }
```
---
## WebP — Recommended for Web and Apps
WebP is the ideal format for most modern integrations. Visual quality is indistinguishable from PNG for QR codes, with **2535% smaller file size**.
**Advantages:**
- Faster loading on web pages
- Lower bandwidth usage in APIs with high volume
- Native support in all modern browsers (Chrome, Safari 14+, Firefox, Edge)
- Lower storage consumption in buckets (S3, GCS, etc.)
**When to use:**
- Display in `<img>` on websites and web applications
- iOS/Android apps (both support WebP)
- When you store the generated QR codes
```json
{ "outputFormat": "webp" }
```
To display in HTML:
```html
<picture>
<source srcset="data:image/webp;base64,{{ qrCodeBase64 }}" type="image/webp">
<img src="data:image/png;base64,{{ fallbackBase64 }}" alt="QR Code">
</picture>
```
---
## SVG — For Lossless Scaling
SVG is a vector format: the QR code is described mathematically, not as pixels. This means it can be resized to any size — from a 16px icon to a 3-meter banner — without loss of quality.
**When to use:**
- Graphic materials (banners, posters, packaging, merchandise)
- Integration with design tools (Figma, Illustrator, Canva)
- High-quality printing without depending on resolution
**Caution:**
- Do not use SVG for email sending (most email clients block SVG for security reasons)
- Some older QR readers may have difficulty with SVG rendered directly via `<img>`
```json
{ "outputFormat": "svg" }
```
To display SVG inline, the `qrCodeBase64` must be decoded first:
```js
const svgString = atob(qrCodeBase64);
document.getElementById('qr').innerHTML = svgString;
```
---
## Size Comparison (real example — 400px, simple URL)
| Format | Typical size |
|---|---|
| PNG | ~812 KB |
| WebP | ~58 KB |
| SVG | ~36 KB |
> Sizes vary depending on content complexity (QR codes with more data have more modules).
---
## Recommendation by Use Case
| Scenario | Recommended format |
|---|---|
| Display on website/app | **WebP** |
| Send by email | **PNG** |
| Graphic printing / design | **SVG** |
| Store in cloud | **WebP** |
| Maximum compatibility | **PNG** |
| No concern about size | **PNG** |

View File

@ -0,0 +1,186 @@
---
title: "How to Generate QR Codes via API: All Supported Types"
description: "Complete guide to generating QR codes via REST API using each available type: URL, Pix, Wi-Fi, vCard, WhatsApp, SMS, email and free text."
keywords: "api qr code, generate qr code api, qr code rest api, qrrapido api, qr code types"
author: "QRRapido"
date: 2026-03-08
lastmod: 2026-03-08
image: ""
---
# How to Generate QR Codes via API: All Supported Types
The QRRapido API supports 8 QR code types. All use the same endpoint — the only difference is the `type` field and the specific fields that make up the `content`.
## Endpoint
```
POST /api/v1/QRManager/generate
X-API-Key: your_key_here
Content-Type: application/json
```
## Base Request Structure
```json
{
"content": "...",
"type": "url",
"size": 400,
"primaryColor": "#000000",
"backgroundColor": "#FFFFFF",
"outputFormat": "png"
}
```
| Field | Type | Default | Description |
|---|---|---|---|
| `content` | string | required | QR code content |
| `type` | string | `"url"` | QR code type (see below) |
| `size` | int | `300` | Size in pixels (1002000) |
| `primaryColor` | string | `"#000000"` | Module color (hex) |
| `backgroundColor` | string | `"#FFFFFF"` | Background color (hex) |
| `outputFormat` | string | `"png"` | Format: `png`, `webp`, `svg` |
---
## Type `url` — Link / URL
The simplest: any valid URL.
```json
{
"type": "url",
"content": "https://yoursite.com/product/123"
}
```
> Use `https://` whenever possible. QR codes with HTTP may be blocked by some modern readers.
---
## Type `pix` — Pix Payment
The `content` must be the **Pix key** of the recipient. The generation produces a static Pix QR code (without connection to the Central Bank — see the dedicated Pix tutorial).
```json
{
"type": "pix",
"content": "contact@yourcompany.com"
}
```
Accepted keys: CPF/CNPJ (digits only), email, phone in `+5511999999999` format, or random key (UUID).
---
## Type `wifi` — Wi-Fi Network
The `content` uses the standard `WIFI:` format:
```json
{
"type": "wifi",
"content": "WIFI:S:NetworkName;T:WPA;P:NetworkPassword;;"
}
```
| Parameter | Meaning | Values |
|---|---|---|
| `S:` | SSID (network name) | text |
| `T:` | Security type | `WPA`, `WEP`, `nopass` |
| `P:` | Password | text |
| `H:` | Hidden network (optional) | `true` / `false` |
---
## Type `vcard` — Business Card
The `content` must be a complete vCard v3:
```json
{
"type": "vcard",
"content": "BEGIN:VCARD\nVERSION:3.0\nFN:John Smith\nORG:Company Inc\nTEL:+15559876543\nEMAIL:john@company.com\nURL:https://company.com\nEND:VCARD"
}
```
---
## Type `whatsapp` — WhatsApp Link
```json
{
"type": "whatsapp",
"content": "https://wa.me/15559876543?text=Hi%2C%20I%27d%20like%20to%20know%20more"
}
```
The number must include country code and area code, without spaces or symbols. The `text` parameter is optional.
---
## Type `email` — Email with subject and body
```json
{
"type": "email",
"content": "mailto:contact@company.com?subject=Subject&body=Initial%20message"
}
```
---
## Type `sms` — Pre-filled SMS
```json
{
"type": "sms",
"content": "sms:+15559876543?body=Hi%2C%20I%20want%20more%20information"
}
```
---
## Type `texto` — Free Text
Any string up to 2048 characters:
```json
{
"type": "texto",
"content": "Table 12 — Priority service available at the main counter."
}
```
---
## Success Response
```json
{
"success": true,
"qrCodeBase64": "iVBORw0KGgo...",
"qrId": "abc123",
"generationTimeMs": 180,
"remainingCredits": 42,
"fromCache": false,
"format": "png",
"mimeType": "image/png"
}
```
To display the image directly in HTML:
```html
<img src="data:image/png;base64,{{ qrCodeBase64 }}" alt="QR Code" />
```
---
## General Tips
- **Colors**: maintain high contrast between `primaryColor` and `backgroundColor`. Avoid yellow on white or light gray on white.
- **Minimum printed size**: use `size` ≥ 400 for printing. For digital use (screens), 300 is sufficient.
- **Cache**: identical requests return `fromCache: true` without consuming additional credit.
- **Rate limit**: the `X-RateLimit-Remaining` and `X-Quota-Remaining` headers in the response indicate your current balance.

View File

@ -0,0 +1,142 @@
---
title: "Static Pix QR Code: How It Works and Developer Responsibilities"
description: "Understand what a static Pix QR Code is, how it is generated by the API, its limitations, and why payment verification is your application's responsibility — not QRRapido's."
keywords: "pix qr code, static pix, pix qr code api, pix qr code integration, pix without central bank"
author: "QRRapido"
date: 2026-03-08
lastmod: 2026-03-08
image: ""
---
# Static Pix QR Code: How It Works and Developer Responsibilities
## What is a Static Pix QR Code?
Pix has two types of QR codes: **static** and **dynamic**.
| | Static Pix | Dynamic Pix |
|---|---|---|
| Amount | Variable (payer decides) | Fixed (set by recipient) |
| Generation | Any system | PSP/Central Bank intermediary |
| Central Bank registration | **No** | Yes |
| Payment notification | **No** | Yes (webhook) |
| Typical use | Donations, simple charges | E-commerce, tracked billing |
The QRRapido API generates **exclusively static Pix QR codes**. This is deliberate and sufficient for most use cases.
---
## How the API Generates the Pix QR Code
When you send a request with `type: "pix"`, the API assembles a string in the **EMV® QR Code** standard (the international standard adopted by Brazil's Central Bank) and generates the image:
```json
{
"type": "pix",
"content": "contact@yourcompany.com"
}
```
The `content` field must contain **only the Pix key** of the recipient. The API handles assembling the EMV payload correctly.
**Accepted keys:**
- Email: `contact@company.com`
- CPF/CNPJ: digits only — `12345678901` or `12345678000195`
- Phone: `+5511999999999`
- Random key (EVP): `123e4567-e89b-12d3-a456-426614174000`
---
## What the API Does NOT Do (and Why This Matters)
> **QRRapido has no integration with the Central Bank, any bank, or any PSP (Payment Service Provider).**
This means:
1. **The API does not know if the payment was made.** It only generates the QR code image. What happens after — whether the customer scanned it, whether the Pix was sent, whether it landed in the right account — is outside the scope of the API.
2. **There is no payment confirmation webhook.** The API does not send notifications when a Pix is received.
3. **The QR code does not expire automatically.** A static Pix QR code is valid indefinitely (or until the Pix key is removed by the recipient at the bank).
4. **There is no transaction traceability.** Two requests with the same key generate the same QR code (with cache). It is not possible to associate a scan with a specific transaction via the API.
---
## Your Application's Responsibility
If you use the API to generate Pix QR codes in a payment flow, **it is your application's responsibility to verify receipt**. The correct ways to do this are:
### Option 1 — Recipient's Bank Pix API
Connect directly to the Pix API of the bank where the key is registered. Most banks offer:
- Query of received charges
- Real-time notification webhook (when the bank supports it)
### Option 2 — PSP / Payment Gateway
Use an intermediary such as Mercado Pago, PagSeguro, Efí Bank, Asaas, etc. They offer dynamic Pix with full control: fixed amount, expiration, webhooks and unique identification per charge.
### Option 3 — Manual Verification
For low volumes or informal contexts (donations, in-person sales), the person responsible for receiving verifies the bank statement manually.
---
## Recommended Flow (with confirmation)
```
Your App QRRapido API Recipient's Bank
| | |
|-- POST /generate (pix) -->| |
|<-- qrCodeBase64 ----------| |
| | |
|-- Shows QR to customer | |
| | |
|-- Query payment ---------------------------------->|
|<-- Received status or not --------------------------|
| | |
|-- Releases product/service| |
```
---
## Complete Request Example
```bash
curl -X POST https://qrrapido.site/api/v1/QRManager/generate \
-H "X-API-Key: your_key_here" \
-H "Content-Type: application/json" \
-d '{
"content": "contact@yourcompany.com",
"type": "pix",
"size": 400,
"outputFormat": "webp"
}'
```
---
## Appropriate Use Cases for Static Pix via API
- Donation QR code on an institutional website
- Digital menu with key for in-person payment
- Batch QR generation for printed materials (flyers, cards)
- Applications where the seller and buyer interact in person and the seller confirms receipt in the bank app
## Use Cases That Require Dynamic Pix (not supported by this API)
- E-commerce with automatic order confirmation
- Fixed-amount billing with expiration
- Invoice issuance linked to payment
- Automated financial reconciliation
---
## Summary
| What the API does | What the API does NOT do |
|---|---|
| Generates the Pix QR code image | Verifies if the payment was made |
| Formats the EMV payload correctly | Sends confirmation webhook |
| Delivers PNG, WebP or SVG | Communicates with the Central Bank or banks |
| Works with any valid Pix key | Guarantees the key belongs to who claims it does |
Correct QR code generation is the API's responsibility. **Payment confirmation is your application's responsibility.**

View File

@ -0,0 +1,89 @@
---
title: "How to Create a Static PIX QR Code"
description: "Learn how to create a static PIX QR Code to receive instant payments easily and securely. Complete guide with QR Rapido."
keywords: "pix qr code, generate pix qr code, static pix, create payment qr code, pix qr code free, pix qr code no fee"
author: "QR Rapido"
date: 2026-01-24
lastmod: 2026-01-24
image: "/images/tutoriais/pix-qr-hero.jpg"
---
# How to Create a Static PIX QR Code: The Complete Guide
**PIX** revolutionized the way payments are made in Brazil. And for those who sell products or receive donations, the **PIX QR Code** is an essential tool. In this tutorial, you will learn how to generate a **Static PIX QR Code** for free using **QR Rapido**, ensuring speed and security in your transactions.
## 💸 What is a Static PIX QR Code?
The PIX Static QR Code is ideal for those who want to receive multiple payments of the same amount or varying amounts using a single code. It always "points" to the same bank account (your PIX key) and can contain additional information such as the recipient's name and city.
**Main advantages:**
- **Does not expire:** Use the same code indefinitely.
- **No fees:** Generation is free and does not depend on intermediaries (gateways).
- **Versatile:** Can have a fixed value set or leave the amount open for the payer to fill in.
- **Ideal for:** Merchants, freelancers, donations, crowdfunding and service providers.
## 🎯 Step by Step to Generate on QR Rapido
**QR Rapido** now has a native and secure tool to generate your PIX code. Follow the steps:
### 1. Access the Generator
Open [QR Rapido](https://qrrapido.site) and in the QR Code type menu, select the **"💸 PIX"** option.
### 2. Fill in the Required Data
For the code to work at any bank, the Central Bank standard requires three pieces of information:
- **PIX Key:** Can be your CPF, CNPJ, Email, Phone or Random Key.
- **Beneficiary Name:** Your name or your company's name (must be the same as on the bank account).
- **City:** The city where the account was opened or where you reside.
> **⚠️ Note:** Fill in the data exactly as registered at your bank to avoid errors during payment.
### 3. Set Amount and Description (Optional)
- **Amount:** If you sell a product with a fixed price (e.g., "Coffee $3.00"), fill in the amount field. If it's a donation or variable payment, leave it blank so the customer can enter the amount.
- **Transaction ID (TxID):** An optional code to help you identify the payment in your statement (e.g., "ORDER01"). If not filled, the system will use the default `***`.
- **Description:** A message that may appear on the payer's confirmation screen (e.g., "Service Payment").
### 4. Generate and Customize
Click **"Generate QR Code"**. You can customize the color and style (rounded corners, for example) to match your brand, but remember: **Keep high contrast** (preferably black on white) to ensure any phone can read it.
### 5. Download
Download the image in **PNG** for use on social media or **PDF/SVG** to print in high quality on signs and posters.
## 🏪 Tip for Merchants and Sellers
If you sell **many different products** and want to speed up payment at the checkout or on shelves, QR Rapido has the ideal solution:
> **Subscribe to our Monthly Plan!**
> With the Premium plan, you can create and manage an **exclusive QR Code for each product** in your catalog. That way, the customer scans the specific product's code and the correct amount is already filled in, avoiding errors and speeding up the sale. [Learn more about the Premium plan](/Pagamento/SelecaoPlano).
## 💡 Professional Tips for Your PIX
### Print with Quality
If you're going to place the QR Code on your store counter, print it at a readable size (at least 2" x 2"). Protect the paper with lamination or use an acrylic display.
### Test Before Distributing
Before printing 1000 flyers or posting on Instagram, do a real test! Open your bank app, read the generated QR Code and transfer a symbolic amount to make sure the data is correct and the money goes to the right account.
### Security
QR Rapido generates the code directly in your browser. We do **not** have access to your bank account and do **not** intermediate the money. The payment goes directly from the customer to your account.
## 🚀 Why use QR Rapido for PIX?
- **EMVCo Compliance:** We use the official international standard, guaranteeing compatibility with all major banking apps.
- **Privacy:** Your sensitive data is not stored.
- **Speed:** Generate your code in seconds, without lengthy registrations.
## Conclusion
Having a PIX QR Code on hand speeds up service and conveys professionalism. With **QR Rapido**, you create yours for free, customize it and start receiving payments right away.
**Ready to receive payments?** [Generate your PIX QR Code now →](https://qrrapido.site)
---
*Questions about generation? [Contact us](https://qrrapido.site/en/Contact) or check our FAQ.*

View File

@ -0,0 +1,108 @@
---
title: "How to Create a QR Code for WhatsApp"
description: "Learn how to create a WhatsApp QR Code that allows users to instantly start a conversation with you"
keywords: "whatsapp qr code, qr code for whatsapp, create whatsapp qr code, whatsapp qr"
author: "QR Rapido"
date: 2025-10-08
lastmod: 2025-10-08
image: "/images/tutoriais/whatsapp-qr-hero.jpg"
---
# How to Create a QR Code for WhatsApp
Creating a **WhatsApp QR Code** is one of the most efficient ways to facilitate direct contact with your customers, friends or followers. With a simple scan, anyone can instantly start a conversation with you without needing to save your number.
## 📱 Why use a QR Code for WhatsApp?
WhatsApp QR Codes are extremely useful for:
- **Businesses**: Facilitating customer service
- **Freelancers**: Speeding up contact with potential clients
- **Events**: Enabling quick networking
- **Marketing**: Increasing conversion in campaigns
## 🎯 Step by Step
### 1. Prepare your number
First, you need to have your number in international format:
```
1 555 987-6543
```
Remove all spaces and dashes:
```
15559876543
```
### 2. Create the WhatsApp URL
The WhatsApp URL follows this pattern:
```
https://wa.me/15559876543
```
You can add a pre-defined message:
```
https://wa.me/15559876543?text=Hi!%20I%20would%20like%20more%20information
```
### 3. Generate the QR Code
Access [QR Rapido](https://qrrapido.site) and:
1. Select the **URL** type
2. Paste the WhatsApp URL
3. Customize the colors (optional)
4. Click **Generate QR Code**
5. Download in high quality
## 💡 Professional Tips
### Customize the Initial Message
Set up an automatic welcome message to improve the experience:
```
https://wa.me/15559876543?text=Hi!%20I%20found%20you%20through%20your%20QR%20Code
```
### Use on Printed Materials
- **Business cards**: Make instant contact easy
- **Flyers and leaflets**: Increase engagement
- **Packaging**: Offer direct customer support
- **Banners**: At events and trade shows
### Monitor the Results
To track how many people scanned your QR Code, consider using a URL shortener with analytics before generating the QR Code.
## ⚠️ Important Cautions
1. **Test before printing**: Always scan to verify it works
2. **Minimum size**: Keep at least 1.2" x 1.2" for easy reading
3. **Adequate contrast**: Use colors that contrast well (black on white is ideal)
4. **Clear message**: Indicate what the QR Code is for
## 🚀 Advantages of using QR Rapido
- ⚡ **Ultra-fast**: Generate in less than 1 second
- 🎨 **Customizable**: Choose colors and styles
- 📥 **High quality**: Download in PNG, SVG and PDF
- 🔒 **Secure**: Your data is not stored
- 💯 **Free**: up to 50 QR codes per day after login
## Conclusion
Creating a QR Code for WhatsApp is simple and can revolutionize the way you communicate with your audience. With QR Rapido, you have everything you need to create professional QR Codes in seconds.
**Ready to start?** [Create your QR Code now →](https://qrrapido.site/en)
---
*Have questions? [Contact us](https://qrrapido.site/en/Contact)!*

View File

@ -0,0 +1,179 @@
---
title: "How to Create a Free WiFi QR Code: Share Your Network in Seconds"
description: "Learn how to create a free WiFi QR Code in a few clicks. Share your network password without typing with our fast and secure generator."
keywords: "wifi qr code, create wifi qr code, free wifi qr code generator, wifi network qr code, share wifi password, free wifi qr code, how to make wifi qr code"
author: "QR Rapido"
date: 2025-10-10
lastmod: 2025-10-10
image: "/images/tutoriais/qr-code-wifi-hero.jpg"
---
# How to Create a Free WiFi QR Code: Share Your Network in Seconds
Tired of typing long and complicated passwords every time a guest asks for your WiFi password? With a **WiFi QR Code**, you can share your network instantly! In this complete tutorial, you'll learn how to create a WiFi QR Code for free in less than 2 minutes.
## Why Use a QR Code for WiFi?
Sharing your WiFi network through a QR Code offers several advantages:
- **Convenience**: Guests connect instantly without typing passwords
- **Security**: No need to say the password out loud or write it down
- **Professionalism**: Ideal for businesses, cafes, restaurants and offices
- **Time saving**: Eliminates typing errors and repeated requests
- **Compatibility**: Works on virtually all modern smartphones
## Step by Step: How to Create Your WiFi QR Code
### Step 1: Select the QR Code Type
Access the generator and choose the **WiFi** option from the list of available QR Code types.
In the dropdown menu, you'll find various options like URL/Link, Plain Text, Business Card, SMS and Email. For this tutorial, select **WiFi**.
### Step 2: Fill in Your WiFi Network Details
Now you need to enter your network details. See the required fields:
#### NetworkName (Network Name) *
Type exactly the name of your WiFi network (SSID). For example: "YourNetworkName"
**Important tip**: The name must be identical to what appears when you search for WiFi networks on your phone. It is case-sensitive!
#### SecurityType (Security Type)
Select the encryption type of your network:
- **WPA Network (most common)**: WPA/WPA2/WPA3 - recommended and most secure
- **WEP (very old)**: Not recommended as it is insecure
- **No password**: For public networks without protection
#### NetworkPassword (Network Password) *
Enter your WiFi network password. Use the eye icon to view and verify you typed it correctly.
**Important**: The password is also case-sensitive. Check it carefully!
#### HiddenNetwork (Hidden Network)
Check this option only if your network is configured as hidden (does not appear in the list of available networks).
### Step 3: Customize Your QR Code (Optional)
Give your QR Code your business's look!
#### Customization Options:
**Primary Color**: Choose the color of the QR Code squares (default: black)
**Background Color**: Set the background color (default: white)
**Size**: Choose from:
- Small (200px) - For digital use
- Medium (300px) - Recommended for printing
- Large (500px) - For banners and posters
**Margin**:
- Compact - Takes up less space
- Normal - Recommended (better readability)
- Wide - For large prints
**Design tip**: Maintain good contrast between the primary color and background to ensure all phones can read the code.
### Step 4: Generate and Download
Click the **"⚡ Generate QR Code Quickly"** button and you're done! Your WiFi QR Code will be generated instantly.
You can:
- Download the image in high quality
- Print and place in visible locations
- Share digitally
- Save to use later
## How Your Guests Will Use the WiFi QR Code
It's very simple! Your guests just need to:
1. Open the phone camera (iOS or Android)
2. Point at the QR Code
3. Tap the notification that appears
4. Connect automatically to the WiFi
**No app download needed!** Most smartphones since 2018 already have built-in QR Code readers in the camera.
## Where to Use Your WiFi QR Code
### For Businesses
- Office reception areas
- Meeting rooms
- Waiting areas
- Coworking spaces
### For Retail
- Restaurant tables
- Café counters
- Stores and boutiques
- Beauty salons
### For Homes
- Entry hallway
- Barbecue area
- Refrigerator (for parties)
- Home office
### For Events
- Event credentials
- Trade show booths
- Conferences
- Weddings and parties
## Security Tips
⚠️ **Important**: Consider creating a separate WiFi network for guests (guest network) if you want to:
- Protect your personal devices
- Limit speed for guests
- Have control over who accesses your network
- Keep your main network private
Many modern routers allow you to easily create guest networks in the settings.
## Frequently Asked Questions (FAQ)
### Does the WiFi QR Code expire?
No! The QR Code works indefinitely as long as the network data (name and password) remains the same.
### Does it work on iPhone and Android?
Yes! It works on virtually all smartphones made after 2018 that have a camera.
### Can I create one for a 5GHz network?
Yes! The process is exactly the same. Just use the correct 5GHz network name.
### Is it safe?
Yes! The QR Code simply makes it easier to enter the data. It is just as safe as sharing the password verbally or in writing.
### Can I edit it after it's created?
You cannot edit the QR Code after it's generated. If you change your WiFi password, you'll need to generate a new QR Code.
### How many people can use the same QR Code?
Unlimited! There is no usage limit for the QR Code.
## Conclusion
Creating a WiFi QR Code is quick, easy and completely free at QR Rapido! In less than 2 minutes you can:
✅ Generate your custom QR Code
✅ Share your network effortlessly
✅ Provide a better experience for guests
✅ Demonstrate professionalism
**Try it now**: [Create my Free WiFi QR Code](/)
---
**Liked this tutorial?** Share it with friends who also want to make WiFi access easier! Also explore our other QR Code types for different needs.
## Other Useful Tutorials
- How to create a QR Code for WhatsApp
- QR Code for Digital Business Cards
- How to create a QR Code for URLs and Links
- Advanced QR Code customization
**Create your free WiFi QR Code now and transform your guests' experience!** 🚀📱

View File

@ -0,0 +1,630 @@
---
title: "QR Code for Real Estate Agents: Complete Guide for Labels and Flyers"
description: "Discover how to use QR Codes on adhesive labels, signs and real estate flyers. Increase your sales with free and professional technology."
keywords: "qr code real estate agent, realtor label, real estate qr code, adhesive labels realtors, promote real estate agent, qr code for sale sign"
author: "QR Rapido"
date: 2025-10-10
lastmod: 2025-10-10
image: "/images/tutoriais/qr-code-corretor-imoveis-hero.jpg"
---
# QR Code for Real Estate Agents: Complete Guide for Labels and Flyers
If you are a real estate agent, you know that **capturing qualified leads** is essential to closing deals. Imagine turning your "For Sale" signs, flyers and adhesive labels into interactive tools that connect clients directly to your WhatsApp, property listing or digital business card — all of this **for free** with QR Codes!
In this complete guide, you will learn how to create and apply professional QR Codes on real estate materials, increasing your conversions and standing out from the competition.
## Why Real Estate Agents Should Use QR Codes?
### **Proven Advantages**
- ✅ **24/7 Lead Generation**: Your sign works for you even while you sleep
- ✅ **Instant Contact**: Client scans and is already on your WhatsApp
- ✅ **Zero Typing**: Eliminates errors when noting numbers
- ✅ **Tracking**: Know how many people were interested
- ✅ **Professionalism**: Demonstrates modernity and innovation
- ✅ **Zero Cost**: Generate unlimited QR Codes for free
- ✅ **Virtual Tour**: Take the client inside the property virtually
### **Market Statistics**
According to real estate industry studies:
- **78%** of buyers research properties on their phones
- **65%** prefer contact via WhatsApp instead of a call
- **43%** scan QR Codes on property signs when they see them
- Agents who use QR Codes have **35% more monthly leads**
---
## Where to Apply QR Codes in Real Estate Marketing
### **1. "For Sale" and "For Rent" Signs**
**The most powerful use!** The sign in front of the property is seen by hundreds of people daily.
**What to put in the QR Code:**
- Direct link to your WhatsApp
- vCard with your complete contacts
- 360° virtual tour of the property
- Detailed property spec sheet (PDF)
- Property video on YouTube
**Professional tip**: Add eye-catching text like:
- "Scan and schedule your visit NOW"
- "Virtual Tour - Point your camera here"
- "Direct WhatsApp to the Agent"
### **2. Adhesive Labels and Tags**
Small labels (2"x2" to 4"x4") are perfect for:
**Applications:**
- Sticking on agency cars
- Fixing on property gates
- Applying to store windows
- Distributing at partner businesses
- Placing in building elevators
**Advantages:**
- Low printing cost
- Easy mass distribution
- Can be replaced quickly
- Great for promotional actions
### **3. Flyers and Leaflets**
Transform paper flyers into digital tools!
**Where to apply:**
- Top right corner (area of greatest attention)
- Center, if it's the main focus
- Back, with highlighted call to action
**Recommended content:**
- Digital property portfolio
- Registration form
- Mortgage calculator
- Complete list of available properties
### **4. Brochures and Real Estate Magazines**
Premium printed materials deserve strategic QR Codes.
**Ideal use:**
- 1 QR per featured property
- QR on the cover for the complete portfolio
- QR on the back cover with your contacts
- QR on each page with more information
### **5. Business Cards**
The classic never goes out of style, but can be enhanced!
**QR Code on the card allows:**
- Save contact automatically (vCard)
- View online portfolio
- Schedule a meeting directly in the calendar
- Send message via WhatsApp
### **6. Email Signature**
Every email you send is an opportunity!
**Include QR Code for:**
- Your complete vCard
- Latest property launch
- Free property valuation
- Scheduling visits
---
## Step by Step: How to Create QR Codes for Agents
Here's how to create **3 essential QR Code types** for real estate agents:
### **Type 1: vCard QR Code (Digital Business Card)**
Perfect for: Business cards, email signature, badges
#### Step 1: Select "Business Card"
Access the generator and choose the **Business Card** option from the type menu.
#### Step 2: Fill in Your Professional Data
**Required information:**
- **Full name**: John Smith
- **Title**: Real Estate Agent License #12345
- **Company**: Success Real Estate
- **Phone**: +1 555 987-6543
- **Email**: john.smith@realestate.com
- **Website**: www.johnsmith.realestate
- **Address**: 1000 Main St - New York, NY
**Strategic optional fields:**
- WhatsApp Business
- Professional Instagram
- LinkedIn
- YouTube channel with virtual tours
#### Step 3: Generate and Download
Click **"Generate QR Code"** and download in high resolution.
**Where to use this QR:**
- Adhesive labels on the car
- Business cards
- Email signature
- Professional badge
---
### **Type 2: Direct WhatsApp QR Code**
Perfect for: Property signs, flyers, ads
#### Step 1: Select "WhatsApp"
In the generator, choose the **WhatsApp** option (or SMS if you prefer).
#### Step 2: Set Up the Pre-Written Message
**Example of an effective message:**
```
Hi! I saw the property sign at [STREET/NEIGHBORHOOD] and would like to schedule a visit. Can you send me more information?
```
**Why a pre-written message works:**
- Client doesn't need to think about what to write
- You already know which property they're talking about
- Increases conversion rate by 80%
#### Step 3: Customize for Each Property
**Important tip**: Create different QR Codes for each property!
Example for a 3-bedroom apartment in downtown:
```
Hi! I saw the sign for the 3-bedroom Apartment downtown (Ref: APT-001). I'd like to know more details and schedule a visit.
```
**Advantage**: You already know exactly which property the client wants to see!
---
### **Type 3: URL QR Code (Virtual Tour / Spec Sheet)**
Perfect for: High-end properties, launches, rural properties
#### Step 1: Prepare the Digital Content
Before creating the QR, you need to have:
**Option A - Virtual Tour:**
- YouTube video of the property
- 360° tour (Google Street View, Matterport)
- Photo gallery on Instagram/Facebook
**Option B - Landing Page:**
- Complete property spec sheet
- High-resolution photos
- Location map
- Mortgage calculator
- Interest registration form
#### Step 2: Shorten the URL (Important!)
Use URL shorteners like:
- bit.ly
- tinyurl.com
**Example:**
- ❌ Long URL: `https://www.myrealestate.com/properties/3-bedroom-downtown-ref-apt001?utm_source=sign`
- ✅ Short URL: `bit.ly/apt-downtown-001`
**Advantage of short URL**: Generates simpler QR Code that's easier to scan
#### Step 3: Create the QR Code
Paste the short URL into the **URL/Link** field and generate the code.
---
## How to Customize Your QR Code Professionally
### **Strategic Color Choices**
**For Traditional Real Estate Agencies:**
- Navy blue + White (trust, seriousness)
- Black + Gold (luxury, exclusivity)
- Dark green + White (growth, stability)
**For Modern Real Estate Agencies:**
- Orange + White (energy, innovation)
- Purple + White (creativity, differentiation)
- Red + White (urgency, action)
**Golden rule**: Always maintain high contrast between primary color and background!
### **Recommended Sizes by Application**
**Street signs (2-15 ft distance):**
- QR Code: 6"x6" or larger
- Resolution: 500px minimum
**Letter/A4 flyers:**
- QR Code: 1.5"x1.5" to 2.5"x2.5"
- Resolution: 300px
**Adhesive labels:**
- QR Code: 2"x2" (sticker size)
- Resolution: 300px
**Business cards:**
- QR Code: 1"x1"
- Resolution: 200px
### **Add Calls to Action (CTA)**
Never place a QR Code alone! Add attractive text:
**Effective examples:**
- 📱 "Point your camera and message me on WhatsApp"
- 🏠 "360° Virtual Tour - Scan Here"
- 💬 "Schedule Your Visit Now"
- 📋 "See Full Photos and Details"
- 🎯 "Save My Contact Automatically"
---
## Advanced Strategies for Agents
### **1. Trackable (Dynamic) QR Codes**
Use dynamic QR Code services to:
- Know how many people scanned
- See scan times
- Identify approximate location
- Change the destination without reprinting
**When to use:**
- Campaigns with many printed materials
- A/B testing different approaches
- Permanent signs on properties
### **2. Multiple QR Codes on the Same Sign**
Large sign? Use 2-3 different QR Codes!
**Example of a complete sign:**
- **QR 1** (top): "Message on WhatsApp"
- **QR 2** (center): "360° Virtual Tour"
- **QR 3** (bottom): "Save My Contact"
**Advantage**: Client chooses the action they prefer
### **3. QR Code + Augmented Reality**
For launches and developments:
- QR Code leads to AR app
- Client points phone and sees the finished building
- Views decorated apartment
- Extremely impactful!
### **4. Seasonal Campaigns**
Create specific QR Codes for:
- **January**: "Plan the New Year - Buy Your Property"
- **June**: "Summer Vacation in Your New Home"
- **November**: "Black Friday Real Estate Sale"
- **December**: "Start the Year in Your Own Home"
### **5. Strategic Partnerships**
Distribute labels with QR Code at:
- Building materials stores
- Architecture offices
- Notary offices
- Neighborhood gyms and restaurants
- Commercial buildings (bulletin boards)
---
## Professional Adhesive Label Templates
### **Template 1: Minimalist Label (2"x2")**
```
┌─────────────────┐
│ [QR CODE] │
│ │
│ John Smith │
│ License #12345
│ (555) 987-6543 │
└─────────────────┘
```
### **Template 2: Highlighted Label (3"x3")**
```
┌─────────────────────┐
│ SELL YOUR PROPERTY │
│ HASSLE-FREE │
│ │
│ [QR CODE] │
│ │
│ "Scan and talk │
│ directly to me" │
│ │
│ John Smith │
│ Licensed Agent │
└─────────────────────┘
```
### **Template 3: Virtual Tour Label (4"x4")**
```
┌───────────────────────────┐
│ 360° VIRTUAL TOUR │
│ Visit this property │
│ from your couch! │
│ │
│ [LARGE QR CODE] │
│ │
│ 📱 Point your camera │
│ │
│ Success Real Estate │
│ (555) 987-6543 │
└───────────────────────────┘
```
---
## Best Printing Practices
### **Recommended Materials**
**For outdoor signs:**
- **UV-resistant vinyl**
- **UV printed banner**
- **ACM (Aluminum Composite)**
Durability: 2-3 years in direct sunlight
**For adhesive labels:**
- **BOPP (Polypropylene) paper**
- **Glossy white vinyl**
- **Coated paper with lamination**
Durability: 6-12 months
**For flyers:**
- **Coated 80lb or 100lb stock**
- **Spot UV varnish on QR Code** (for emphasis)
### **Testing Before Mass Printing**
⚠️ **ALWAYS do this:**
1. Print 1 sample at actual size
2. Test with 5 different phones
3. Test at different distances
4. Test in low light
5. Only then print large quantities
**Phones to test:**
- iPhone (updated iOS)
- Samsung (Android)
- Other Android brands (popular models)
### **Where to Print**
**Quick print shops:**
- Adhesive labels: 100 units for $15-50
- A5 flyers: 1000 units for $50-150
**Online (cheaper):**
- Vistaprint
- Overnight Prints
- Sticker Mule
**Tip**: Get quotes from 3 different places!
---
## Common Mistakes Agents Should Avoid
### ❌ **Mistake 1: QR Code Too Small**
**Problem**: On street signs viewed from a distance, a small QR won't work
**Solution**: Minimum 6"x6" for outdoor signs
### ❌ **Mistake 2: Colors with Low Contrast**
**Problem**: Yellow QR on white background won't scan
**Solution**: Always use dark colors on light backgrounds (or vice versa)
### ❌ **Mistake 3: Not Testing Before Printing**
**Problem**: Print 1000 flyers and discover the QR doesn't work
**Solution**: Always test with a pilot print
### ❌ **Mistake 4: Broken or Temporary URL**
**Problem**: Property link expires, QR becomes useless
**Solution**: Use permanent URLs or editable dynamic QR
### ❌ **Mistake 5: No Usage Instructions**
**Problem**: Older person doesn't know what to do with the QR
**Solution**: Add "Point your phone camera here"
### ❌ **Mistake 6: Single QR Code for All Properties**
**Problem**: Don't know which property generated the lead
**Solution**: Create a specific QR for each property
### ❌ **Mistake 7: Low Quality Materials**
**Problem**: Label fades in 1 month in the sun
**Solution**: Invest in UV-resistant materials
---
## Success Stories
### **Case 1: Agent in New York**
**Strategy**: Placed QR Code on all 15 property signs
**Results:**
- 127 scans in the first month
- 34 WhatsApp conversations
- 8 visits scheduled
- 2 sales closed
**ROI**: Invested $150 in labels, earned $28,000 in commissions
### **Case 2: Real Estate Agency**
**Strategy**: Flyers with QR for virtual tour of new development
**Results:**
- 5,000 flyers distributed
- 890 virtual tour accesses
- 156 registered interested buyers
- 23 apartments sold in pre-sale
### **Case 3: Independent Agent**
**Strategy**: Adhesive labels at partner businesses
**Results:**
- 200 labels distributed at 40 locations
- 67 new contacts in 3 months
- 12 property valuations scheduled
- 3 exclusive listings captured
---
## Complementary Tools
### **Digital Content Creation**
- **Canva**: Create label and flyer layouts
- **Matterport**: 360° virtual tours
- **YouTube**: Host property videos
- **Google Drive**: Property spec sheet PDFs
### **Lead Management**
- **Bitly**: Shorten URLs and track clicks
- **Google Analytics**: Monitor traffic
- **WhatsApp Business**: Organize conversations
- **CRM tools**: Real estate CRM solutions
### **Online Printing**
- **Vistaprint**: Labels and flyers
- **Overnight Prints**: Signs and banners
- **Sticker Mule**: Premium stickers
---
## Frequently Asked Questions
### **Do I need to pay to create a QR Code?**
No! At QR Rapido you create unlimited QR Codes for free. You only pay if you want premium features like advanced tracking.
### **Does the QR Code work forever?**
Static QR Codes (free) work forever, but cannot be edited. Dynamic QR Codes (paid) can be edited even after printing.
### **Which QR type to use on property signs?**
I recommend **WhatsApp** with a pre-written message. That way the client already starts the conversation knowing which property they're asking about.
### **Can I put the agency logo on the QR?**
Yes, but carefully! Logos that are too large can make it harder to read. Keep the logo small (maximum 20% of the QR).
### **How many people scan QR Codes on signs?**
According to research, between 5-15% of people who see the sign scan the QR. In busy areas, this can generate dozens of leads per month.
### **Does the QR Code work at night?**
Yes, as long as there is some lighting. For best results, illuminate the sign.
### **Can I use the same QR on multiple materials?**
You can, but it's not recommended. Create different QR Codes to know where each lead came from (sign, flyer, label, etc.).
### **How do I know if the QR is working?**
Test immediately after creating! Use your own phone camera and at least 2 other people's phones to make sure.
---
## Professional Agent Checklist
Before printing, verify:
- [ ] QR Code tested on at least 3 different phones
- [ ] Appropriate size for scanning distance
- [ ] High contrast between QR and background
- [ ] Clear call to action ("Scan here")
- [ ] Visible contact information (name, license, phone)
- [ ] Short URL if using a link (easier to scan)
- [ ] Durable printing material (especially for outdoor use)
- [ ] Safety margin around the QR (minimum 0.4")
---
## Conclusion
QR Codes are the **cheapest and most effective tool** for modern agents to capture qualified leads. With an investment of less than $100 in labels and flyers, you can:
✅ Capture leads 24 hours a day
✅ Facilitate instant contact via WhatsApp
✅ Show impressive virtual tours
✅ Track which materials generate the most results
✅ Stand out from the conservative competition
**The real estate market is increasingly digital. Those who don't adapt get left behind.**
---
## Get Started Now!
**Create your first real estate agent QR Code for free:**
1. [Generate WhatsApp QR Code](/) - For property signs
2. [Generate vCard QR Code](/) - For business cards
3. [Generate URL QR Code](/) - For virtual tours
**Turn your signs and flyers into lead generation machines!** 🏠📱🚀
---
## Bonus Materials
- 📋 Pre-written WhatsApp message template
- 🎨 Editable label templates
- 📊 QR Code tracking spreadsheet by property
- 🎯 Professional printing checklist
**Want to stand out in the real estate market? Use QR Codes strategically and watch your results multiply!**

View File

@ -18,6 +18,7 @@ namespace QRRapidoApp.Controllers
[Route("Developer/docs")]
[Route("es-PY/Developer/docs")]
[Route("es/Developer/docs")]
[Route("en/Developer/docs")]
public async Task<IActionResult> Index()
{
var culture = GetCulture();
@ -29,6 +30,7 @@ namespace QRRapidoApp.Controllers
[Route("Developer/docs/{slug}")]
[Route("es-PY/Developer/docs/{slug}")]
[Route("es/Developer/docs/{slug}")]
[Route("en/Developer/docs/{slug}")]
public async Task<IActionResult> Article(string slug)
{
var culture = GetCulture();
@ -57,6 +59,8 @@ namespace QRRapidoApp.Controllers
if (path.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase)) return "es-PY";
if (path.StartsWith("/es/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/es", StringComparison.OrdinalIgnoreCase)) return "es";
if (path.StartsWith("/en/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/en", StringComparison.OrdinalIgnoreCase)) return "en";
return "pt-BR";
}
}

View File

@ -7,6 +7,10 @@ using System.Security.Claims;
namespace QRRapidoApp.Controllers
{
[Authorize]
[Route("Developer")]
[Route("es-PY/Developer")]
[Route("es/Developer")]
[Route("en/Developer")]
public class DeveloperController : Controller
{
private readonly IUserService _userService;
@ -23,7 +27,7 @@ namespace QRRapidoApp.Controllers
_logger = logger;
}
[HttpGet]
[HttpGet("")]
public async Task<IActionResult> Index()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
@ -36,7 +40,7 @@ namespace QRRapidoApp.Controllers
return View(user);
}
[HttpPost]
[HttpPost("GenerateKey")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> GenerateKey(string keyName)
{
@ -46,7 +50,7 @@ namespace QRRapidoApp.Controllers
if (string.IsNullOrWhiteSpace(keyName) || keyName.Length > 50)
{
TempData["Error"] = "Nome da chave inválido (máx. 50 caracteres).";
return RedirectToAction(nameof(Index));
return Redirect(GetDevUrl());
}
var user = await _userService.GetUserAsync(userId);
@ -55,7 +59,7 @@ namespace QRRapidoApp.Controllers
if (user.ApiKeys.Count(k => k.IsActive) >= 5)
{
TempData["Error"] = "Limite de 5 chaves ativas atingido. Revogue uma antes de criar outra.";
return RedirectToAction(nameof(Index));
return Redirect(GetDevUrl());
}
var (rawKey, prefix) = await _userService.GenerateApiKeyAsync(userId, keyName.Trim());
@ -67,7 +71,7 @@ namespace QRRapidoApp.Controllers
return RedirectToAction(nameof(Index));
}
[HttpGet]
[HttpGet("Pricing")]
public async Task<IActionResult> Pricing()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
@ -80,7 +84,7 @@ namespace QRRapidoApp.Controllers
return View();
}
[HttpPost]
[HttpPost("SubscribeApi")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SubscribeApi(string planTier)
{
@ -92,7 +96,7 @@ namespace QRRapidoApp.Controllers
tier == ApiPlanTier.Enterprise)
{
TempData["Error"] = "Plano inválido selecionado.";
return RedirectToAction(nameof(Pricing));
return Redirect(GetDevUrl("Pricing"));
}
try
@ -105,11 +109,11 @@ namespace QRRapidoApp.Controllers
{
_logger.LogError(ex, "Error creating API subscription checkout for user {UserId}", userId);
TempData["Error"] = "Erro ao criar sessão de pagamento. Tente novamente.";
return RedirectToAction(nameof(Pricing));
return Redirect(GetDevUrl("Pricing"));
}
}
[HttpPost]
[HttpPost("RevokeKey")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RevokeKey(string prefix)
{
@ -119,7 +123,7 @@ namespace QRRapidoApp.Controllers
if (string.IsNullOrWhiteSpace(prefix))
{
TempData["Error"] = "Chave inválida.";
return RedirectToAction(nameof(Index));
return Redirect(GetDevUrl());
}
var revoked = await _userService.RevokeApiKeyAsync(userId, prefix);
@ -142,7 +146,20 @@ namespace QRRapidoApp.Controllers
if (path.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase)) return "es-PY";
if (path.StartsWith("/es/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/es", StringComparison.OrdinalIgnoreCase)) return "es";
if (path.StartsWith("/en/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/en", StringComparison.OrdinalIgnoreCase)) return "en";
return "pt-BR";
}
private string GetDevUrl(string action = "")
{
var base_ = GetCulture() switch {
"es-PY" => "/es-PY/Developer",
"es" => "/es/Developer",
"en" => "/en/Developer",
_ => "/Developer"
};
return string.IsNullOrEmpty(action) ? base_ : $"{base_}/{action}";
}
}
}

View File

@ -39,6 +39,7 @@ namespace QRRapidoApp.Controllers
[HttpGet]
[Route("/")]
[Route("es-PY")]
[Route("en")]
public async Task<IActionResult> Index(string? qrType = null)
{
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
@ -116,36 +117,44 @@ namespace QRRapidoApp.Controllers
[Route("pix")]
[Route("es-PY/pix")]
[Route("en/pix")]
public async Task<IActionResult> Pix() => await Index("pix");
[Route("wifi")]
[Route("es-PY/wifi")]
[Route("en/wifi")]
public async Task<IActionResult> Wifi() => await Index("wifi");
[Route("vcard")]
[Route("es-PY/vcard")]
[Route("en/vcard")]
public async Task<IActionResult> VCard() => await Index("vcard");
[Route("whatsapp")]
[Route("es-PY/whatsapp")]
[Route("en/whatsapp")]
public async Task<IActionResult> WhatsApp() => await Index("whatsapp");
[Route("email")]
[Route("es-PY/email")]
[Route("en/email")]
public async Task<IActionResult> Email() => await Index("email");
[Route("sms")]
[Route("es-PY/sms")]
[Route("en/sms")]
public async Task<IActionResult> Sms() => await Index("sms");
[Route("texto")]
[Route("text")]
[Route("es-PY/texto")]
[Route("es-PY/text")]
[Route("en/text")]
public async Task<IActionResult> Text() => await Index("text");
[Route("url")]
[Route("es-PY/url")]
[Route("en/url")]
public async Task<IActionResult> UrlType() => await Index("url");
public IActionResult Privacy()

View File

@ -29,9 +29,11 @@ namespace QRRapidoApp.Controllers
// Portuguese: /tutoriais/{slug} (canonical, no prefix)
// Spanish: /es-PY/tutoriais/{slug}
// English: /en/tutoriais/{slug}
[Route("tutoriais/{slug}")]
[Route("es-PY/tutoriais/{slug}")]
[Route("es/tutoriais/{slug}")]
[Route("en/tutoriais/{slug}")]
public async Task<IActionResult> Article(string slug, string? culture = null)
{
try
@ -40,6 +42,7 @@ namespace QRRapidoApp.Controllers
var reqPath = Request.Path.Value ?? "";
culture ??= reqPath.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase) ? "es-PY"
: reqPath.StartsWith("/es/", StringComparison.OrdinalIgnoreCase) ? "es"
: reqPath.StartsWith("/en/", StringComparison.OrdinalIgnoreCase) ? "en"
: "pt-BR";
var article = await _markdownService.GetArticleAsync(slug, culture);
@ -89,9 +92,11 @@ namespace QRRapidoApp.Controllers
// Portuguese: /tutoriais (canonical, no prefix)
// Spanish: /es-PY/tutoriais
// English: /en/tutoriais
[Route("tutoriais")]
[Route("es-PY/tutoriais")]
[Route("es/tutoriais")]
[Route("en/tutoriais")]
public async Task<IActionResult> Index(string? culture = null)
{
try
@ -100,6 +105,7 @@ namespace QRRapidoApp.Controllers
var reqPath = Request.Path.Value ?? "";
culture ??= reqPath.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase) ? "es-PY"
: reqPath.StartsWith("/es/", StringComparison.OrdinalIgnoreCase) ? "es"
: reqPath.StartsWith("/en/", StringComparison.OrdinalIgnoreCase) ? "en"
: "pt-BR";
var articles = await _markdownService.GetAllArticlesAsync(culture);
@ -112,9 +118,13 @@ namespace QRRapidoApp.Controllers
ViewBag.UserName = User.Identity?.Name ?? "";
_adDisplayService.SetViewBagAds(ViewBag);
ViewBag.Title = culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR";
ViewBag.Title = culture == "pt-BR" ? "Tutoriais QR Code"
: culture == "en" ? "QR Code Tutorials"
: "Tutoriales Código QR";
ViewBag.Description = culture == "pt-BR"
? "Aprenda a criar e usar QR Codes com nossos tutoriais completos"
: culture == "en"
? "Learn how to create and use QR Codes with our complete step-by-step tutorials"
: "Aprende a crear y usar códigos QR con nuestros tutoriales completos";
ViewBag.Culture = culture;

View File

@ -16,6 +16,16 @@ namespace QRRapidoApp.Filters
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// [AllowAnonymous] on the action bypasses this filter
var hasAllowAnonymous = context.ActionDescriptor.EndpointMetadata
.OfType<Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute>()
.Any();
if (hasAllowAnonymous)
{
await next();
return;
}
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ApiKeyAuthorizeAttribute>>();
try

View File

@ -10,13 +10,14 @@ namespace QRRapidoApp.Middleware
/// - "/" → Retorna 200 OK em Português (canonical)
/// - "/pt-BR" ou "/pt-BR/*" → Redireciona 301 para "/" ou "/*" (sem prefixo)
/// - "/es-PY" ou "/es-PY/*" → Retorna 200 OK em Espanhol (mantém URL)
/// - "/en" ou "/en/*" → Retorna 200 OK em Inglês (mantém URL)
/// - "/pix", "/wifi", etc. → Retorna 200 OK em Português (sem redirect)
/// </summary>
public class LanguageRedirectionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LanguageRedirectionMiddleware> _logger;
private readonly string[] _supportedCultures = { "pt-BR", "es-PY", "es" };
private readonly string[] _supportedCultures = { "pt-BR", "es-PY", "es", "en" };
private const string DefaultCulture = "pt-BR";
private const string CookieName = ".AspNetCore.Culture";
@ -84,7 +85,15 @@ namespace QRRapidoApp.Middleware
return;
}
// Handle aliases or unsupported cultures (e.g. /en/, /pt/)
// Supported: en (English)
if (string.Equals(firstSegment, "en", StringComparison.OrdinalIgnoreCase))
{
SetCulture(context, "en");
await _next(context);
return;
}
// Handle aliases or unsupported cultures (e.g. /en-US/, /pt/)
if (TryHandleCultureAliasOrUnknown(context, firstSegment, segments))
{
return;
@ -108,13 +117,14 @@ namespace QRRapidoApp.Middleware
{
// Map known aliases to canonical forms
if (string.Equals(firstSegment, "es-py", StringComparison.OrdinalIgnoreCase))
{
return RedirectToLanguage(context, "es-PY", segments);
}
// For anything else that looks like a culture (en, pt, fr, etc.)
// but isn't explicitly es-PY, we redirect to the canonical (no prefix)
// This prevents 404s for /en/, /pt-BR/ (redirects to /), etc.
if (string.Equals(firstSegment, "en-us", StringComparison.OrdinalIgnoreCase) ||
string.Equals(firstSegment, "en-gb", StringComparison.OrdinalIgnoreCase))
return RedirectToLanguage(context, "en", segments);
// For anything else that looks like a culture (pt, fr, de, etc.)
// redirect to the canonical Portuguese URL (no prefix).
return RedirectToCanonical(context, segments);
}

View File

@ -226,6 +226,7 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
new CultureInfo("pt-BR"),
new CultureInfo("es-PY"),
new CultureInfo("es"),
new CultureInfo("en"),
};
options.DefaultRequestCulture = new RequestCulture("pt-BR", "pt-BR");
@ -463,7 +464,7 @@ app.MapHealthChecks("/healthcheck");
// Language routes (must be first for WEB)
app.MapControllerRoute(
name: "localized",
pattern: "{culture:regex(^(pt-BR|es-PY|es)$)}/{controller=Home}/{action=Index}/{id?}");
pattern: "{culture:regex(^(pt-BR|es-PY|es|en)$)}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "api_v1",

View File

@ -9,7 +9,7 @@ namespace QRRapidoApp.Providers
// First check if the middleware has already determined the culture (e.g. for static routes like /es-PY/pix)
if (httpContext.Items.TryGetValue("Culture", out var cultureItem) && cultureItem is string cultureFromMiddleware)
{
var supportedCultures = new[] { "pt-BR", "es-PY" };
var supportedCultures = new[] { "pt-BR", "es-PY", "en" };
if (supportedCultures.Contains(cultureFromMiddleware))
{
return Task.FromResult<ProviderCultureResult?>(new ProviderCultureResult(cultureFromMiddleware, cultureFromMiddleware));
@ -24,7 +24,7 @@ namespace QRRapidoApp.Providers
if (!string.IsNullOrEmpty(culture))
{
// Map the supported cultures
var supportedCultures = new[] { "pt-BR", "es-PY" };
var supportedCultures = new[] { "pt-BR", "es-PY", "en" };
if (supportedCultures.Contains(culture))
{
return Task.FromResult<ProviderCultureResult?>(new ProviderCultureResult(culture, culture));

File diff suppressed because it is too large Load Diff

View File

@ -2104,7 +2104,7 @@
</data>
<!-- Tutorial Section -->
<data name="ViewTutorials" xml:space="preserve">
<value>Ver Tutoriales</value>
<value>Tutoriales QR</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Aprende Más</value>
@ -2113,7 +2113,7 @@
<value>Guías completas sobre códigos QR</value>
</data>
<data name="ViewAllTutorials" xml:space="preserve">
<value>Ver Todos los Tutoriales</value>
<value>Tutoriales QR</value>
</data>
<data name="RealEstateAndBrokers" xml:space="preserve">
<value>Inmobiliaria y Corredores</value>

View File

@ -2257,7 +2257,7 @@
</data>
<!-- Tutorial Section -->
<data name="ViewTutorials" xml:space="preserve">
<value>Ver Tutoriais</value>
<value>Tutoriais QR</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Aprenda Mais</value>
@ -2266,7 +2266,7 @@
<value>Guias completos sobre QR Codes</value>
</data>
<data name="ViewAllTutorials" xml:space="preserve">
<value>Ver Todos os Tutoriais</value>
<value>Tutoriais QR</value>
</data>
<data name="RealEstateAndBrokers" xml:space="preserve">
<value>Imóveis e Corretores</value>

View File

@ -3,19 +3,19 @@
ViewData["Title"] = Model.Metadata.Title + " — QRRapido Docs";
Layout = "~/Views/Shared/_Layout.cshtml";
var culture = ViewBag.Culture as string ?? "pt-BR";
var isEs = culture == "es-PY";
var devBase = isEs ? "/es-PY/Developer" : "/Developer";
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
var devBase = isEn ? "/en/Developer" : isEs ? "/es-PY/Developer" : "/Developer";
var slug = ViewBag.Slug as string;
string T(string pt, string es) => isEs ? es : pt;
string T(string pt, string es, string en) => isEn ? en : isEs ? es : pt;
}
<div class="container mt-4 mb-5">
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="@devBase">@T("Portal do Desenvolvedor", "Portal del Desarrollador")</a></li>
<li class="breadcrumb-item"><a href="@devBase">@T("Portal do Desenvolvedor", "Portal del Desarrollador", "Developer Portal")</a></li>
<li class="breadcrumb-item"><a href="@devBase/docs">Docs</a></li>
<li class="breadcrumb-item active" aria-current="page">@Model.Metadata.Title</li>
</ol>
@ -27,7 +27,7 @@
<header class="mb-4">
<h1 class="mb-3">@Model.Metadata.Title</h1>
<p class="text-muted small">
<i class="fas fa-clock me-1"></i> @Model.Metadata.ReadingTimeMinutes @T("min de leitura", "min de lectura")
<i class="fas fa-clock me-1"></i> @Model.Metadata.ReadingTimeMinutes @T("min de leitura", "min de lectura", "min read")
&nbsp;·&nbsp;
<i class="fas fa-calendar me-1"></i> @Model.Metadata.Date.ToString("dd/MM/yyyy")
</p>
@ -40,7 +40,7 @@
</div>
<footer class="mt-5 pt-3 border-top">
<small class="text-muted">@T("Última atualização:", "Última actualización:") @Model.Metadata.LastMod.ToString("dd/MM/yyyy")</small>
<small class="text-muted">@T("Última atualização:", "Última actualización:", "Last updated:") @Model.Metadata.LastMod.ToString("dd/MM/yyyy")</small>
</footer>
</article>
@ -48,20 +48,20 @@
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-primary text-white py-2">
<h6 class="mb-0"><i class="fas fa-rocket me-2"></i>@T("Links Rápidos", "Links Pya'e")</h6>
<h6 class="mb-0"><i class="fas fa-rocket me-2"></i>@T("Links Rápidos", "Links Pya'e", "Quick Links")</h6>
</div>
<div class="list-group list-group-flush">
<a href="@devBase" class="list-group-item list-group-item-action small">
<i class="fas fa-key me-2 text-primary"></i>@T("Minhas Chaves de API", "Mis Claves de API")
<i class="fas fa-key me-2 text-primary"></i>@T("Minhas Chaves de API", "Mis Claves de API", "My API Keys")
</a>
<a href="/api/docs" target="_blank" class="list-group-item list-group-item-action small">
<i class="fas fa-code me-2 text-success"></i>Swagger / OpenAPI
</a>
<a href="@devBase/Pricing" class="list-group-item list-group-item-action small">
<i class="fas fa-arrow-up me-2 text-warning"></i>@T("Planos de API", "Planes de API")
<i class="fas fa-arrow-up me-2 text-warning"></i>@T("Planos de API", "Planes de API", "API Plans")
</a>
<a href="@devBase/docs" class="list-group-item list-group-item-action small">
<i class="fas fa-book me-2 text-info"></i>@T("Todos os Tutoriais", "Todos los Tutoriales")
<i class="fas fa-book me-2 text-info"></i>@T("Todos os Tutoriais", "Todos los Tutoriales", "All Tutorials")
</a>
</div>
</div>
@ -70,7 +70,7 @@
{
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-2">
<h6 class="mb-0"><i class="fas fa-bookmark me-2 text-secondary"></i>@T("Outros Tutoriais", "Otros Tutoriales")</h6>
<h6 class="mb-0"><i class="fas fa-bookmark me-2 text-secondary"></i>@T("Outros Tutoriais", "Otros Tutoriales", "Other Tutorials")</h6>
</div>
<div class="list-group list-group-flush">
@foreach (var related in Model.RelatedArticles)

View File

@ -3,11 +3,11 @@
ViewData["Title"] = "Docs & Tutoriais para Desenvolvedores";
Layout = "~/Views/Shared/_Layout.cshtml";
var culture = ViewBag.Culture as string ?? "pt-BR";
var isEs = culture == "es-PY";
var devBase = isEs ? "/es-PY/Developer" : "/Developer";
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
var devBase = isEn ? "/en/Developer" : isEs ? "/es-PY/Developer" : "/Developer";
string T(string pt, string es) => isEs ? es : pt;
string T(string pt, string es, string en) => isEn ? en : isEs ? es : pt;
}
<div class="container mt-4 mb-5">
@ -16,13 +16,13 @@
<div class="d-flex align-items-center">
<div class="me-3"><i class="fas fa-book-open fa-2x text-primary"></i></div>
<div>
<h1 class="h3 mb-0">@T("Docs & Tutoriais", "Docs & Tutoriales")</h1>
<p class="text-muted mb-0 small">@T("Guias técnicos para integrar e usar a API QRRapido.", "Guías técnicas para integrar ha usar la API QRRapido.")</p>
<h1 class="h3 mb-0">@T("Docs & Tutoriais", "Docs & Tutoriales", "Docs & Tutorials")</h1>
<p class="text-muted mb-0 small">@T("Guias técnicos para integrar e usar a API QRRapido.", "Guías técnicas para integrar ha usar la API QRRapido.", "Technical guides to integrate and use the QRRapido API.")</p>
</div>
</div>
<div class="d-flex gap-2">
<a href="@devBase" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-key me-1"></i> @T("Minhas Chaves", "Mis Claves")
<i class="fas fa-key me-1"></i> @T("Minhas Chaves", "Mis Claves", "My Keys")
</a>
<a href="/api/docs" target="_blank" class="btn btn-outline-success btn-sm">
<i class="fas fa-code me-1"></i> Swagger
@ -42,11 +42,11 @@
<p class="card-text text-muted flex-grow-1 small">@article.Description</p>
<div class="mb-3">
<small class="text-muted">
<i class="fas fa-clock me-1"></i> @article.ReadingTimeMinutes @T("min de leitura", "min de lectura")
<i class="fas fa-clock me-1"></i> @article.ReadingTimeMinutes @T("min de leitura", "min de lectura", "min read")
</small>
</div>
<a href="@devBase/docs/@article.Slug" class="btn btn-primary btn-sm">
@T("Ler", "Leer") <i class="fas fa-arrow-right ms-1"></i>
@T("Ler", "Leer", "Read") <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
@ -58,7 +58,7 @@
{
<div class="text-center py-5">
<i class="fas fa-book fa-3x text-muted opacity-25 mb-3"></i>
<p class="text-muted">@T("Nenhum artigo encontrado.", "Ningún artículo encontrado.")</p>
<p class="text-muted">@T("Nenhum artigo encontrado.", "Ningún artículo encontrado.", "No articles found.")</p>
</div>
}
</div>

View File

@ -12,11 +12,11 @@
var revokedKeys = Model.ApiKeys.Where(k => !k.IsActive).OrderByDescending(k => k.CreatedAt).ToList();
var baseUrl = Context.Request.Scheme + "://" + Context.Request.Host;
var culture = ViewBag.Culture as string ?? "pt-BR";
var isEs = culture == "es-PY";
var docsBase = isEs ? "/es-PY/Developer" : "/Developer";
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
var docsBase = isEn ? "/en/Developer" : isEs ? "/es-PY/Developer" : "/Developer";
string T(string pt, string es) => isEs ? es : pt;
string T(string pt, string es, string en = null) => isEn && en != null ? en : isEs ? es : pt;
}
<div class="container mt-4 mb-5">
@ -28,29 +28,30 @@
<i class="fas fa-code fa-2x text-primary"></i>
</div>
<div>
<h1 class="h3 mb-0">@T("Portal do Desenvolvedor", "Portal del Desarrollador")</h1>
<h1 class="h3 mb-0">@T("Portal do Desenvolvedor", "Portal del Desarrollador", "Developer Portal")</h1>
<p class="text-muted mb-0 small">
@T("Gerencie suas chaves de API e integre o QR Rapido nas suas aplicações.",
"Gestioná tus claves de API ha integrá QR Rapido en tus aplicaciones.")
"Gestioná tus claves de API ha integrá QR Rapido en tus aplicaciones.",
"Manage your API keys and integrate QR Rapido into your applications.")
@{
var tier = Model.ApiSubscription?.EffectiveTier ?? QRRapidoApp.Models.ApiPlanTier.Free;
var tierLabel = tier == QRRapidoApp.Models.ApiPlanTier.Free
? "<span class='badge bg-secondary ms-2'>Free</span>"
: $"<span class='badge bg-primary ms-2'>{tier}</span>";
}
@T("Plano atual:", "Plan actual:") @Html.Raw(tierLabel)
@T("Plano atual:", "Plan actual:", "Current plan:") @Html.Raw(tierLabel)
</p>
</div>
</div>
<div class="d-flex gap-2">
<a href="@docsBase/docs" class="btn btn-outline-info btn-sm">
<i class="fas fa-book me-1"></i> @T("Tutoriais", "Tutoriales")
<i class="fas fa-book me-1"></i> Docs API
</a>
<a href="/api/docs" target="_blank" class="btn btn-outline-success btn-sm">
<i class="fas fa-code me-1"></i> Swagger
</a>
<a href="@docsBase/Pricing" class="btn btn-outline-primary btn-sm">
<i class="fas fa-arrow-up me-1"></i> @T("Ver Planos de API", "Ver Planes de API")
<i class="fas fa-arrow-up me-1"></i> @T("Ver Planos de API", "Ver Planes de API", "API Plans")
</a>
</div>
</div>

View File

@ -5,24 +5,25 @@
var currentTier = ViewBag.CurrentTier as ApiPlanTier? ?? ApiPlanTier.Free;
var errorMsg = TempData["Error"] as string;
var culture = ViewBag.Culture as string ?? "pt-BR";
var isEs = culture == "es-PY";
var devBase = isEs ? "/es-PY/Developer" : "/Developer";
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
var devBase = isEn ? "/en/Developer" : isEs ? "/es-PY/Developer" : "/Developer";
string T(string pt, string es) => isEs ? es : pt;
string T(string pt, string es, string en = null) => isEn && en != null ? en : isEs ? es : pt;
}
<div class="container mt-4 mb-5">
<div class="text-center mb-5">
<h1 class="h2 fw-bold">@T("Planos de API", "Planes de API")</h1>
<h1 class="h2 fw-bold">@T("Planos de API", "Planes de API", "API Plans")</h1>
<p class="text-muted">
@T("Escolha o plano que melhor se adapta à sua integração.",
"Elegí el plan porã que mejor se adapte a tu integración.")
"Elegí el plan porã que mejor se adapte a tu integración.",
"Choose the plan that best fits your integration.")
<br>
@T("Todos os planos incluem a", "Todos los planes incluyen la")
@T("Todos os planos incluem a", "Todos los planes incluyen la", "All plans include the")
<strong>API REST</strong>
@T("com autenticação por chave.", "con autenticación por clave.")
@T("com autenticação por chave.", "con autenticación por clave.", "with key authentication.")
</p>
@if (!string.IsNullOrEmpty(errorMsg))
{
@ -174,7 +175,7 @@
<div class="text-center mt-3">
<a href="@devBase" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>@T("Voltar ao Portal", "Volver al Portal")
<i class="fas fa-arrow-left me-2"></i>@T("Voltar ao Portal", "Volver al Portal", "Back to Portal")
</a>
</div>
</div>

View File

@ -18,33 +18,38 @@
var appEnvironment = Configuration["App:Environment"] ?? HostEnvironment?.EnvironmentName ?? "Unknown";
var secretsLoaded = Configuration.GetValue<bool>("App:SecretsLoaded");
// SEO: Determine if current page is Spanish (for URL building)
// SEO: Determine current culture from URL
var requestPath = Context.Request.Path.Value ?? "/";
var isSpanish = requestPath.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase);
var isSpanish = requestPath.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase)
|| requestPath.StartsWith("/es/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/es", StringComparison.OrdinalIgnoreCase);
var isEnglish = requestPath.StartsWith("/en/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/en", StringComparison.OrdinalIgnoreCase);
// Get path without culture prefix for building alternate URLs
var pathWithoutCulture = requestPath;
if (isSpanish && requestPath.Length > 6)
{
pathWithoutCulture = requestPath.Substring(6); // Remove "/es-PY"
}
else if (isSpanish)
{
pathWithoutCulture = "/";
}
if (requestPath.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase))
pathWithoutCulture = requestPath.Length > 6 ? requestPath.Substring(6) : "/";
else if (requestPath.StartsWith("/es/", StringComparison.OrdinalIgnoreCase))
pathWithoutCulture = requestPath.Length > 3 ? requestPath.Substring(3) : "/";
else if (requestPath.StartsWith("/en/", StringComparison.OrdinalIgnoreCase))
pathWithoutCulture = requestPath.Length > 3 ? requestPath.Substring(3) : "/";
if (string.IsNullOrEmpty(pathWithoutCulture)) pathWithoutCulture = "/";
// Canonical URL - for Portuguese it's without prefix, for Spanish it's with /es-PY
var canonicalUrl = isSpanish
// Canonical URL
var canonicalUrl = isEnglish
? $"https://qrrapido.site/en{(pathWithoutCulture == "/" ? "" : pathWithoutCulture)}"
: isSpanish
? $"https://qrrapido.site/es-PY{(pathWithoutCulture == "/" ? "" : pathWithoutCulture)}"
: $"https://qrrapido.site{pathWithoutCulture}";
// Alternate URLs for hreflang
var ptUrl = $"https://qrrapido.site{pathWithoutCulture}";
var esUrl = $"https://qrrapido.site/es-PY{(pathWithoutCulture == "/" ? "" : pathWithoutCulture)}";
var enUrl = $"https://qrrapido.site/en{(pathWithoutCulture == "/" ? "" : pathWithoutCulture)}";
// Culture prefix for internal links
var culturePrefix = isSpanish ? "/es-PY" : "";
var culturePrefix = isEnglish ? "/en" : isSpanish ? "/es-PY" : "";
if (User?.Identity?.IsAuthenticated == true)
{
@ -86,6 +91,7 @@
<!-- Hreflang for multilingual SEO -->
<link rel="alternate" hreflang="pt-BR" href="@ptUrl">
<link rel="alternate" hreflang="es-PY" href="@esUrl">
<link rel="alternate" hreflang="en" href="@enUrl">
<link rel="alternate" hreflang="x-default" href="@ptUrl">
<!-- Open Graph -->
@ -319,7 +325,7 @@
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" data-lang="pt-BR">🇧🇷 Português (Brasil)</a></li>
<li><a class="dropdown-item" href="#" data-lang="es-PY">🇵🇾 Español (Paraguay)</a></li>
@* <li><a class="dropdown-item" href="#" data-lang="en">🇺🇸 English</a></li> *@
<li><a class="dropdown-item" href="#" data-lang="en">🇺🇸 English</a></li>
</ul>
</div>

View File

@ -2,6 +2,9 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewData["Title"] = Model.Metadata.Title;
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
string T(string pt, string es, string en) => isEn ? en : isEs ? es : pt;
var baseUrl = "https://qrrapido.site";
var articleUrl = $"{baseUrl}/{ViewBag.Culture}/tutoriais/{ViewBag.Slug}";
}
@ -84,7 +87,7 @@
{
"@@type": "ListItem",
"position": 2,
"name": "@(ViewBag.Culture == "pt-BR" ? "Tutoriais" : "Tutoriales")",
"name": "@T("Tutoriais", "Tutoriales", "Tutorials")",
"item": "@baseUrl/@ViewBag.Culture/tutoriais"
},
{
@ -103,7 +106,7 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/@ViewBag.Culture">Home</a></li>
<li class="breadcrumb-item"><a href="/@ViewBag.Culture/tutoriais">@(ViewBag.Culture == "pt-BR" ? "Tutoriais" : "Tutoriales")</a></li>
<li class="breadcrumb-item"><a href="/@ViewBag.Culture/tutoriais">@T("Tutoriais", "Tutoriales", "Tutorials")</a></li>
<li class="breadcrumb-item active" aria-current="page">@Model.Metadata.Title</li>
</ol>
</nav>
@ -118,7 +121,7 @@
<div class="text-muted mb-3">
<span><i class="fas fa-user"></i> @Model.Metadata.Author</span> |
<span><i class="fas fa-calendar"></i> @Model.Metadata.Date.ToString("dd MMM yyyy")</span> |
<span><i class="fas fa-clock"></i> @Model.Metadata.ReadingTimeMinutes min @(ViewBag.Culture == "pt-BR" ? "de leitura" : "de lectura")</span>
<span><i class="fas fa-clock"></i> @Model.Metadata.ReadingTimeMinutes min @T("de leitura", "de lectura", "read")</span>
</div>
@if (!string.IsNullOrEmpty(Model.Metadata.Image))
@ -138,7 +141,7 @@
<footer class="mt-5 pt-4 border-top">
<p class="text-muted">
<small>
@(ViewBag.Culture == "pt-BR" ? "Última atualização:" : "Última actualización:")
@T("Última atualização:", "Última actualización:", "Last updated:")
@Model.Metadata.LastMod.ToString("dd MMM yyyy HH:mm")
</small>
</p>
@ -170,7 +173,7 @@
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-book"></i>
@(ViewBag.Culture == "pt-BR" ? "Artigos Relacionados" : "Artículos Relacionados")
@T("Artigos Relacionados", "Artículos Relacionados", "Related Articles")
</h5>
</div>
<div class="list-group list-group-flush">
@ -193,15 +196,15 @@
<div class="card-body">
<h5 class="card-title">
<i class="fas fa-crown"></i>
@(ViewBag.Culture == "pt-BR" ? "Seja Premium!" : "¡Hazte Premium!")
@T("Seja Premium!", "¡Hazte Premium!", "Go Premium!")
</h5>
<p class="card-text">
@(ViewBag.Culture == "pt-BR"
? "QR codes ilimitados, sem anúncios e recursos avançados."
: "Códigos QR ilimitados, sin anuncios y características avanzadas.")
@T("QR codes ilimitados, sem anúncios e recursos avançados.",
"Códigos QR ilimitados, sin anuncios y características avanzadas.",
"Unlimited QR codes, no ads and advanced features.")
</p>
<a href="/@ViewBag.Culture/Pagamento/SelecaoPlano" class="btn btn-light btn-block">
@(ViewBag.Culture == "pt-BR" ? "Conhecer Planos" : "Conocer Planes")
@T("Conhecer Planos", "Conocer Planes", "View Plans")
</a>
</div>
</div>
@ -212,21 +215,21 @@
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-link"></i>
@(ViewBag.Culture == "pt-BR" ? "Links Úteis" : "Enlaces Útiles")
@T("Links Úteis", "Enlaces Útiles", "Useful Links")
</h5>
</div>
<div class="list-group list-group-flush">
<a href="/@ViewBag.Culture" class="list-group-item list-group-item-action">
<i class="fas fa-qrcode"></i>
@(ViewBag.Culture == "pt-BR" ? "Gerar QR Code" : "Generar Código QR")
@T("Gerar QR Code", "Generar Código QR", "Generate QR Code")
</a>
<a href="/@ViewBag.Culture/FAQ" class="list-group-item list-group-item-action">
<i class="fas fa-question-circle"></i>
@(ViewBag.Culture == "pt-BR" ? "Perguntas Frequentes" : "Preguntas Frecuentes")
@T("Perguntas Frequentes", "Preguntas Frecuentes", "FAQ")
</a>
<a href="/@ViewBag.Culture/Contact" class="list-group-item list-group-item-action">
<i class="fas fa-envelope"></i>
@(ViewBag.Culture == "pt-BR" ? "Contato" : "Contacto")
@T("Contato", "Contacto", "Contact")
</a>
</div>
</div>

View File

@ -1,6 +1,10 @@
@model List<QRRapidoApp.Models.ArticleMetadata>
@{
ViewData["Title"] = ViewBag.Culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR";
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
string T(string pt, string es, string en) => isEn ? en : isEs ? es : pt;
ViewData["Title"] = T("Tutoriais QR Code", "Tutoriales Código QR", "QR Code Tutorials");
Layout = "~/Views/Shared/_Layout.cshtml";
}
@ -10,12 +14,12 @@
<div class="col-12">
<h1 class="display-4">
<i class="fas fa-book"></i>
@(ViewBag.Culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR")
@T("Tutoriais QR Code", "Tutoriales Código QR", "QR Code Tutorials")
</h1>
<p class="lead text-muted">
@(ViewBag.Culture == "pt-BR"
? "Aprenda tudo sobre QR Codes com nossos tutoriais completos e passo a passo"
: "Aprende todo sobre códigos QR con nuestros tutoriales completos paso a paso")
@T("Aprenda tudo sobre QR Codes com nossos tutoriais completos e passo a passo",
"Aprende todo sobre códigos QR con nuestros tutoriales completos paso a paso",
"Learn everything about QR Codes with our complete step-by-step tutorials")
</p>
</div>
</div>
@ -45,7 +49,7 @@
<a href="/@ViewBag.Culture/tutoriais/@article.Slug"
class="btn btn-primary btn-block">
@(ViewBag.Culture == "pt-BR" ? "Ler Tutorial" : "Leer Tutorial")
@T("Ler Tutorial", "Leer Tutorial", "Read Tutorial")
<i class="fas fa-arrow-right ml-1"></i>
</a>
</div>
@ -58,9 +62,9 @@
{
<div class="alert alert-info" role="alert">
<i class="fas fa-info-circle"></i>
@(ViewBag.Culture == "pt-BR"
? "Nenhum tutorial disponível no momento. Volte em breve!"
: "No hay tutoriales disponibles en este momento. ¡Vuelve pronto!")
@T("Nenhum tutorial disponível no momento. Volte em breve!",
"No hay tutoriales disponibles en este momento. ¡Vuelve pronto!",
"No tutorials available at the moment. Check back soon!")
</div>
}
@ -70,18 +74,18 @@
<div class="card bg-primary text-white">
<div class="card-body text-center py-5">
<h3 class="mb-3">
@(ViewBag.Culture == "pt-BR"
? "Pronto para criar seu QR Code?"
: "¿Listo para crear tu código QR?")
@T("Pronto para criar seu QR Code?",
"¿Listo para crear tu código QR?",
"Ready to create your QR Code?")
</h3>
<p class="lead mb-4">
@(ViewBag.Culture == "pt-BR"
? "Gere QR codes profissionais em segundos com nossa ferramenta ultrarrápida!"
: "¡Genera códigos QR profesionales en segundos con nuestra herramienta ultrarrápida!")
@T("Gere QR codes profissionais em segundos com nossa ferramenta ultrarrápida!",
"¡Genera códigos QR profesionales en segundos con nuestra herramienta ultrarrápida!",
"Generate professional QR codes in seconds with our ultra-fast tool!")
</p>
<a href="/@ViewBag.Culture" class="btn btn-light btn-lg">
<i class="fas fa-qrcode"></i>
@(ViewBag.Culture == "pt-BR" ? "Criar QR Code Agora" : "Crear Código QR Ahora")
@T("Criar QR Code Agora", "Crear Código QR Ahora", "Create QR Code Now")
</a>
</div>
</div>

View File

@ -2,31 +2,33 @@
// SEO Strategy:
// - Portuguese (pt-BR): URLs without prefix (/, /pix, /wifi, etc.) - canonical
// - Spanish (es-PY): URLs with /es-PY prefix (/es-PY, /es-PY/pix, etc.)
// - English (en): URLs with /en prefix (/en, /en/pix, etc.)
document.addEventListener('DOMContentLoaded', function () {
const languageDropdownItems = document.querySelectorAll('.dropdown-item[data-lang]');
const currentLangSpan = document.getElementById('current-lang');
// Cultures that use a URL prefix
const prefixedCultures = ['es-PY', 'en'];
// Get current culture from URL
function getCurrentCulture() {
const pathSegments = window.location.pathname.split('/').filter(segment => segment);
// Check if first segment is es-PY (Spanish)
if (pathSegments.length > 0 && pathSegments[0].toLowerCase() === 'es-py') {
return 'es-PY';
if (pathSegments.length > 0) {
const first = pathSegments[0].toLowerCase();
if (first === 'es-py') return 'es-PY';
if (first === 'en') return 'en';
}
// Default is Portuguese (no prefix in URL)
return 'pt-BR';
return 'pt-BR'; // Default — no prefix
}
// Update current language display in header
function updateCurrentLanguageDisplay(culture) {
const langMap = {
'pt-BR': 'PT',
'es-PY': 'ES'
'es-PY': 'ES',
'en': 'EN'
};
if (currentLangSpan) {
currentLangSpan.textContent = langMap[culture] || 'PT';
}
@ -38,25 +40,24 @@ document.addEventListener('DOMContentLoaded', function () {
const queryString = window.location.search;
const hash = window.location.hash;
// Get path segments, removing any culture prefix
// Strip any existing culture prefix
let pathSegments = currentPath.split('/').filter(segment => segment);
// Remove existing culture prefix if present (es-PY or pt-BR)
if (pathSegments.length > 0) {
const firstSegment = pathSegments[0].toLowerCase();
if (firstSegment === 'es-py' || firstSegment === 'pt-br') {
const first = pathSegments[0].toLowerCase();
if (first === 'es-py' || first === 'pt-br' || first === 'en') {
pathSegments.shift();
}
}
// Build new path based on selected culture
const rest = pathSegments.length > 0 ? '/' + pathSegments.join('/') : '';
let newPath;
if (newCulture === 'pt-BR') {
// Portuguese: no prefix (canonical URLs)
newPath = pathSegments.length > 0 ? '/' + pathSegments.join('/') : '/';
newPath = rest || '/';
} else {
// Spanish: add /es-PY prefix
newPath = '/es-PY' + (pathSegments.length > 0 ? '/' + pathSegments.join('/') : '');
// Spanish / English: add culture prefix
newPath = '/' + newCulture + rest;
}
return newPath + queryString + hash;