fix: ajustes de idioma e pipelina
Some checks failed
Deploy ASP.NET MVC to OCI / build-and-deploy (push) Has been cancelled

This commit is contained in:
Ricardo Carneiro 2025-09-12 13:45:37 -03:00
parent c76a4ea5ab
commit be3a93f90d
18 changed files with 1029 additions and 28 deletions

127
.gitea/workflows/deploy.yml Normal file
View File

@ -0,0 +1,127 @@
name: Deploy ASP.NET MVC to OCI
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
build-and-deploy:
runs-on: localACDC
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Debug - List files
run: |
echo "=== Arquivos na raiz ==="
ls -la
echo "=== Procurando Dockerfile ==="
find . -name "Dockerfile" -o -name "dockerfile" -type f
echo "=== Estrutura do projeto ==="
tree -L 3 || find . -type d -name "convertit"
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/arm64
push: true
tags: |
registry.redecarneir.us/convertit:latest
registry.redecarneir.us/convertit:${{ github.sha }}
cache-from: |
type=local,src=/tmp/.buildx-cache
type=registry,ref=registry.redecarneir.us/convertit:cache
cache-to: |
type=local,dest=/tmp/.buildx-cache-new,mode=max
type=registry,ref=registry.redecarneir.us/convertit:cache,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
- name: Deploy to OCI Server
uses: appleboy/ssh-action@v1.0.3
with:
host: 129.146.116.218
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 22
timeout: 60s
script: |
# Para o container anterior da aplicação (se existir)
docker stop convertit || true
docker rm convertit || true
# Remove imagem antiga
docker rmi registry.redecarneir.us/convertit:latest || true
# Puxa nova imagem
docker pull registry.redecarneir.us/convertit:latest
# Executa o novo container na porta 80
docker run -d \
--name convertit \
--restart unless-stopped \
--add-host="k3ss1:172.17.0.1" \
--add-host="k3sw2:172.17.0.1" \
-p 80:8080 \
--memory=2g \
--cpus=1.5 \
--health-cmd="curl -f http://localhost:8080/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
--health-start-period=60s \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ASPNETCORE_URLS="http://+:8080" \
-e DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
-e DOTNET_USE_POLLING_FILE_WATCHER=true \
-e DOTNET_EnableDiagnostics=0 \
-e DOTNET_RUNNING_IN_CONTAINER=true \
registry.redecarneir.us/convertit:latest
# Limpa imagens não utilizadas
docker image prune -f
# Verifica se o container está rodando
docker ps | grep convertit
# Testa se a aplicação está respondendo na porta 80
sleep 10
curl -f http://localhost:80 || echo "Aplicação pode estar inicializando..."
- name: Verify deployment
uses: appleboy/ssh-action@v1.0.3
with:
host: 129.146.116.218
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 22
script: |
echo "=== Status dos containers ==="
docker ps -a | grep convertit
echo "=== Logs da aplicação (últimas 20 linhas) ==="
docker logs convertit --tail 20
echo "=== Teste de conectividade ==="
curl -I http://localhost:80 || echo "Aplicação ainda não está acessível"

89
CLAUDE.md Normal file
View File

@ -0,0 +1,89 @@
# Claude Context - Convert-It Online
## Projeto Overview
Convert-It Online é uma aplicação web ASP.NET Core que oferece ferramentas gratuitas de conversão de arquivos com suporte a múltiplos idiomas.
## URLs Amigáveis ao SEO - SISTEMA IMPLEMENTADO
### Estrutura de URLs Traduzidas
**IMPORTANTE**: O projeto agora possui URLs totalmente traduzidas para melhor SEO.
#### URLs por Idioma:
**Português (pt-BR):**
- Ferramentas de Texto: `/pt-BR/ferramentas-de-texto/conversor-de-maiusculas-minusculas`
- Conversores de Imagem: `/pt-BR/conversores-de-imagem/jpg-para-webp`
**Espanhol (es-MX, es-CL, es-PY):**
- Herramientas de Texto: `/es-MX/herramientas-de-texto/conversor-de-mayusculas-minusculas`
- Convertidores de Imagen: `/es-MX/convertidores-de-imagen/jpg-a-webp`
### Arquitetura do Sistema de URLs
1. **UrlTranslationService**: Mapeia URLs traduzidas para controllers originais
2. **RouteConstraints personalizados**:
- `LocalizedAreaRouteConstraint`
- `LocalizedControllerRouteConstraint`
3. **Resources (.resx)**: Contêm as traduções das URLs em `UrlTextTools`, `UrlImageConverters`, etc.
## Estrutura de Localização
### Idiomas Suportados
- `pt-BR` (Português Brasil) - idioma padrão
- `es-MX` (Espanhol México)
- `es-CL` (Espanhol Chile)
- `es-PY` (Espanhol Paraguai)
### Arquivos de Resources
- `SharedResource.{culture}.resx` - Resources compartilhados incluindo traduções de URL
- `Areas/*/Views/*/Index.{culture}.resx` - Resources específicos de views
- `Views/Home/*.{culture}.resx` - Resources das páginas principais
## Areas e Controllers
### TextTools
- **Controller**: `CaseConverterController`
- **Funcionalidade**: Conversão de texto (maiúsculas, minúsculas, primeira maiúscula)
- **URL PT**: `/pt-BR/ferramentas-de-texto/conversor-de-maiusculas-minusculas`
- **URL ES**: `/es-MX/herramientas-de-texto/conversor-de-mayusculas-minusculas`
### ImageConverters
- **Controller**: `JpgToWebpController`
- **Funcionalidade**: Conversão de JPG para WebP
- **URL PT**: `/pt-BR/conversores-de-imagem/jpg-para-webp`
- **URL ES**: `/es-MX/convertidores-de-imagen/jpg-a-webp`
## Desenvolvimento Guidelines
### Adicionando Novos Conversores
1. **Controller**: Criar em `Areas/{AreaName}/Controllers/`
2. **URL Translation**: Adicionar mapeamento em `UrlTranslationService`
3. **Resources**: Adicionar traduções em todos os arquivos `.resx` relevantes
4. **Testing**: Verificar URLs traduzidas em todos os idiomas
### Manutenção URLs SEO
- ✅ SEMPRE verificar se novas funcionalidades seguem padrão de URL traduzida
- ✅ Manter compatibilidade com URLs antigas (fallback implementado)
- ✅ Validar traduções em todos os idiomas suportados
- ✅ Usar nomes descritivos e friendly nas URLs traduzidas
### Padrões de Nomenclatura
- URLs sempre em minúsculas
- Usar hífens (-) para separar palavras
- Manter consistência entre idiomas
- Refletir a funcionalidade real da ferramenta
## Comandos Úteis
- Build: `dotnet build`
- Run: `dotnet run`
- Test local: navegar para `https://localhost:59345/pt-BR/ferramentas-de-texto/conversor-de-maiusculas-minusculas`
## Status Atual
✅ Sistema de URLs traduzidas implementado e funcionando
✅ Suporte completo para 4 idiomas
✅ RouteConstraints personalizados implementados
✅ Fallback para URLs antigas mantido
🔄 Aguardando testes finais
---
*Mantenha este arquivo atualizado quando adicionar novos conversores ou idiomas.*

View File

@ -1,10 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36414.22 d17.14
VisualStudioVersion = 17.14.36414.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Convert-It", "Convert-It.csproj", "{D8E3BC66-82F6-0019-7082-DF2342D15CEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deploy", "deploy", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
.gitea\workflows\deploy.yml = .gitea\workflows\deploy.yml
Dockerfile = Dockerfile
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

58
Dockerfile Normal file
View File

@ -0,0 +1,58 @@
# Dockerfile self-contained para m<>xima performance
FROM --platform=linux/arm64 mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM --platform=linux/arm64 mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
# Copiar apenas arquivos de projeto primeiro (melhor cache)
COPY ["Convert-It.csproj", "./"]
# Restore com configura<72><61>es otimizadas para ARM64
RUN dotnet restore "./Convert-It.csproj" \
--runtime linux-arm64 \
--no-cache
# Copiar c<>digo fonte
COPY . .
WORKDIR "/src"
# Build otimizado
RUN dotnet build "./Convert-It.csproj" \
-c $BUILD_CONFIGURATION \
-o /app/build \
--runtime linux-arm64 \
--no-restore
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
# Publish self-contained para eliminar cold start
RUN dotnet publish "./Convert-It.csproj" \
-c $BUILD_CONFIGURATION \
-o /app/publish \
--runtime linux-arm64 \
--no-restore \
--self-contained true \
/p:PublishTrimmed=true \
/p:PublishSingleFile=false
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 AS final
WORKDIR /app
COPY --from=publish /app/publish .
# Vari<72>veis de ambiente otimizadas para produ<64><75>o
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
ENV ASPNETCORE_ENVIRONMENT=Production
ENV DOTNET_EnableDiagnostics=0
# Healthcheck simples
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["./Convert-It"]

View File

@ -0,0 +1,41 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Convert_It_Online.Services;
using System.Globalization;
namespace Convert_It_Online.Extensions
{
public static class HtmlHelperExtensions
{
public static string LocalizedUrl(this IHtmlHelper htmlHelper, string area, string controller, string action = "Index")
{
var context = htmlHelper.ViewContext.HttpContext;
var urlTranslationService = context.RequestServices.GetRequiredService<IUrlTranslationService>();
// Get current culture
var cultureFeature = context.Features.Get<Microsoft.AspNetCore.Localization.IRequestCultureFeature>();
var culture = cultureFeature?.RequestCulture.UICulture ?? new CultureInfo("pt-BR");
// Get localized URL
var localizedUrl = urlTranslationService.GetLocalizedUrl(area, controller, action, culture);
return localizedUrl;
}
public static IHtmlContent LocalizedLink(this IHtmlHelper htmlHelper, string linkText, string area, string controller, string action = "Index", object? htmlAttributes = null)
{
var url = htmlHelper.LocalizedUrl(area, controller, action);
var attributes = "";
if (htmlAttributes != null)
{
var props = htmlAttributes.GetType().GetProperties();
var attrList = props.Select(p => $"{p.Name.Replace('_', '-')}=\"{p.GetValue(htmlAttributes)}\"");
attributes = " " + string.Join(" ", attrList);
}
var html = $"<a href=\"{url}\"{attributes}>{linkText}</a>";
return new HtmlString(html);
}
}
}

View File

@ -0,0 +1,72 @@
using Convert_It_Online.Services;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Convert_It_Online.Middleware
{
public class UrlTranslationMiddleware
{
private readonly RequestDelegate _next;
private readonly IUrlTranslationService _urlTranslationService;
private readonly ILogger<UrlTranslationMiddleware> _logger;
public UrlTranslationMiddleware(RequestDelegate next, IUrlTranslationService urlTranslationService, ILogger<UrlTranslationMiddleware> logger)
{
_next = next;
_urlTranslationService = urlTranslationService;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var path = context.Request.Path.Value;
if (!string.IsNullOrEmpty(path))
{
_logger.LogInformation($"Processing path: {path}");
// Pattern: /{culture}/{translatedArea}/{translatedController}[/{action}]
var match = Regex.Match(path, @"^/([a-z]{2}-[A-Z]{2})/([^/]+)/([^/]+)(?:/([^/]+))?");
if (match.Success)
{
var cultureName = match.Groups[1].Value;
var translatedArea = match.Groups[2].Value;
var translatedController = match.Groups[3].Value;
var action = match.Groups[4].Success ? match.Groups[4].Value : "Index";
_logger.LogInformation($"Matched: culture={cultureName}, area={translatedArea}, controller={translatedController}, action={action}");
try
{
var culture = new CultureInfo(cultureName);
var originalArea = _urlTranslationService.GetOriginalArea(translatedArea, culture);
var originalController = _urlTranslationService.GetOriginalController(translatedController, culture);
_logger.LogInformation($"Translations: originalArea={originalArea}, originalController={originalController}");
if (originalArea != null && originalController != null)
{
// Rewrite the path to use original controller names
var newPath = $"/{cultureName}/{originalArea}/{originalController}";
if (!string.IsNullOrEmpty(action) && action != "Index")
{
newPath += $"/{action}";
}
_logger.LogInformation($"Rewriting path from {path} to {newPath}");
context.Request.Path = newPath;
}
}
catch (CultureNotFoundException)
{
_logger.LogWarning($"Culture {cultureName} not found");
// Continue with original path if culture is not supported
}
}
}
await _next(context);
}
}
}

View File

@ -1,53 +1,53 @@
using System.Globalization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Localization.Routing;
using Microsoft.Extensions.Options;
using Convert_It_Online.Services;
using Convert_It_Online.Middleware;
// 1. Builder e Serviços
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLocalization();
builder.Services.AddSingleton<IUrlTranslationService, UrlTranslationService>();
var supportedCultures = new[] { "pt-BR", "es-MX", "es-CL", "es-PY" };
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
options.DefaultRequestCulture = new RequestCulture("pt-BR");
options.SupportedCultures = supportedCultures.Select(c => new CultureInfo(c)).ToList();
options.SupportedUICultures = supportedCultures.Select(c => new CultureInfo(c)).ToList();
});
builder.Services.AddControllersWithViews()
.AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix);
var app = builder.Build();
// 2. Configuração de Localização (RequestLocalizationOptions)
var supportedCultures = new[] { "pt-BR", "es-MX", "es-CL", "es-PY" };
var localizationOptions = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("pt-BR"),
SupportedCultures = supportedCultures.Select(c => new CultureInfo(c)).ToList(),
SupportedUICultures = supportedCultures.Select(c => new CultureInfo(c)).ToList()
};
var localizationOptions = app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value;
localizationOptions.RequestCultureProviders.Clear();
localizationOptions.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider());
localizationOptions.RequestCultureProviders.Insert(1, new QueryStringRequestCultureProvider());
localizationOptions.RequestCultureProviders.Insert(2, new AcceptLanguageHeaderRequestCultureProvider());
localizationOptions.RequestCultureProviders.Insert(1, new AcceptLanguageHeaderRequestCultureProvider());
localizationOptions.RequestCultureProviders.Insert(2, new QueryStringRequestCultureProvider());
// 3. Pipeline de Middlewares (na ordem correta)
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseMiddleware<UrlTranslationMiddleware>();
app.UseRouting();
app.UseRequestLocalization(localizationOptions);
app.UseAuthorization();
// 4. Mapeamento de Rotas (com cultura)
app.MapControllerRoute(
name: "areaRoute",
pattern: "{culture:length(2,5)}/{area:exists}/{controller=Home}/{action=Index}/{id?}");
@ -61,5 +61,4 @@ app.MapControllerRoute(
pattern: "{controller=Home}/{action=Index}/{id?}",
defaults: new { culture = "pt-BR" });
// 5. Execução
app.Run();

View File

@ -0,0 +1,37 @@
using Convert_It_Online.Services;
using Microsoft.AspNetCore.Routing;
using System.Globalization;
namespace Convert_It_Online.Routing
{
public class LocalizedAreaRouteConstraint : IRouteConstraint
{
private readonly IUrlTranslationService _urlTranslationService;
public LocalizedAreaRouteConstraint(IUrlTranslationService urlTranslationService)
{
_urlTranslationService = urlTranslationService;
}
public bool Match(HttpContext? httpContext, IRouter? route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.TryGetValue("translatedArea", out var value) && value is string areaName)
{
if (values.TryGetValue("culture", out var cultureValue) && cultureValue is string cultureName)
{
var culture = new CultureInfo(cultureName);
var originalArea = _urlTranslationService.GetOriginalArea(areaName, culture);
if (originalArea != null)
{
values["area"] = originalArea;
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,37 @@
using Convert_It_Online.Services;
using Microsoft.AspNetCore.Routing;
using System.Globalization;
namespace Convert_It_Online.Routing
{
public class LocalizedControllerRouteConstraint : IRouteConstraint
{
private readonly IUrlTranslationService _urlTranslationService;
public LocalizedControllerRouteConstraint(IUrlTranslationService urlTranslationService)
{
_urlTranslationService = urlTranslationService;
}
public bool Match(HttpContext? httpContext, IRouter? route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.TryGetValue("translatedController", out var value) && value is string controllerName)
{
if (values.TryGetValue("culture", out var cultureValue) && cultureValue is string cultureName)
{
var culture = new CultureInfo(cultureName);
var originalController = _urlTranslationService.GetOriginalController(controllerName, culture);
if (originalController != null)
{
values["controller"] = originalController;
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Globalization;
namespace Convert_It_Online.Services
{
public interface IUrlTranslationService
{
string TranslateArea(string area, CultureInfo culture);
string TranslateController(string controller, CultureInfo culture);
string? GetOriginalArea(string translatedArea, CultureInfo culture);
string? GetOriginalController(string translatedController, CultureInfo culture);
string GetLocalizedUrl(string area, string controller, string action, CultureInfo culture);
}
}

View File

@ -0,0 +1,144 @@
using System.Globalization;
namespace Convert_It_Online.Services
{
public class UrlTranslationService : IUrlTranslationService
{
private readonly Dictionary<string, Dictionary<string, string>> _areaTranslations;
private readonly Dictionary<string, Dictionary<string, string>> _controllerTranslations;
private readonly Dictionary<string, Dictionary<string, string>> _reverseAreaTranslations;
private readonly Dictionary<string, Dictionary<string, string>> _reverseControllerTranslations;
public UrlTranslationService()
{
_areaTranslations = new Dictionary<string, Dictionary<string, string>>
{
["pt-BR"] = new Dictionary<string, string>
{
["TextTools"] = "ferramentas-de-texto",
["ImageConverters"] = "conversores-de-imagem"
},
["es-MX"] = new Dictionary<string, string>
{
["TextTools"] = "herramientas-de-texto",
["ImageConverters"] = "convertidores-de-imagen"
},
["es-CL"] = new Dictionary<string, string>
{
["TextTools"] = "herramientas-de-texto",
["ImageConverters"] = "convertidores-de-imagen"
},
["es-PY"] = new Dictionary<string, string>
{
["TextTools"] = "herramientas-de-texto",
["ImageConverters"] = "convertidores-de-imagen"
}
};
_controllerTranslations = new Dictionary<string, Dictionary<string, string>>
{
["pt-BR"] = new Dictionary<string, string>
{
["CaseConverter"] = "conversor-de-maiusculas-minusculas",
["JpgToWebp"] = "jpg-para-webp"
},
["es-MX"] = new Dictionary<string, string>
{
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
["JpgToWebp"] = "jpg-a-webp"
},
["es-CL"] = new Dictionary<string, string>
{
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
["JpgToWebp"] = "jpg-a-webp"
},
["es-PY"] = new Dictionary<string, string>
{
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
["JpgToWebp"] = "jpg-a-webp"
}
};
// Create reverse mappings for lookup
_reverseAreaTranslations = CreateReverseMappings(_areaTranslations);
_reverseControllerTranslations = CreateReverseMappings(_controllerTranslations);
}
private Dictionary<string, Dictionary<string, string>> CreateReverseMappings(
Dictionary<string, Dictionary<string, string>> original)
{
var reverse = new Dictionary<string, Dictionary<string, string>>();
foreach (var culture in original.Keys)
{
reverse[culture] = new Dictionary<string, string>();
foreach (var kvp in original[culture])
{
reverse[culture][kvp.Value] = kvp.Key;
}
}
return reverse;
}
public string TranslateArea(string area, CultureInfo culture)
{
var cultureName = culture.Name;
if (_areaTranslations.TryGetValue(cultureName, out var areaDict) &&
areaDict.TryGetValue(area, out var translatedArea))
{
return translatedArea;
}
return area.ToLowerInvariant();
}
public string TranslateController(string controller, CultureInfo culture)
{
var cultureName = culture.Name;
if (_controllerTranslations.TryGetValue(cultureName, out var controllerDict) &&
controllerDict.TryGetValue(controller, out var translatedController))
{
return translatedController;
}
return controller.ToLowerInvariant();
}
public string? GetOriginalArea(string translatedArea, CultureInfo culture)
{
var cultureName = culture.Name;
if (_reverseAreaTranslations.TryGetValue(cultureName, out var areaDict) &&
areaDict.TryGetValue(translatedArea, out var originalArea))
{
return originalArea;
}
return null;
}
public string? GetOriginalController(string translatedController, CultureInfo culture)
{
var cultureName = culture.Name;
if (_reverseControllerTranslations.TryGetValue(cultureName, out var controllerDict) &&
controllerDict.TryGetValue(translatedController, out var originalController))
{
return originalController;
}
return null;
}
public string GetLocalizedUrl(string area, string controller, string action, CultureInfo culture)
{
var translatedArea = TranslateArea(area, culture);
var translatedController = TranslateController(controller, culture);
return $"/{culture.Name}/{translatedArea}/{translatedController}";
}
}
}

122
SharedResource.es-CL.resx Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!-- Main Page Content - Chilean Spanish -->
<data name="PageTitle" xml:space="preserve">
<value>Convertidores Gratuitos Online</value>
</data>
<data name="Subtitle" xml:space="preserve">
<value>Convierte textos, imágenes y documentos gratis. Herramientas online bacanes y seguras, sin necesidad de instalar nada.</value>
</data>
<data name="ChooseConverter" xml:space="preserve">
<value>Elige tu Conversor</value>
</data>
<data name="TextToolsTitle" xml:space="preserve">
<value>Herramientas de Texto</value>
</data>
<data name="TextToolsDescription" xml:space="preserve">
<value>Convierte, formatea y manipula textos súper fácil.</value>
</data>
<data name="ImageToolsTitle" xml:space="preserve">
<value>Conversores de Imagen</value>
</data>
<data name="ImageToolsDescription" xml:space="preserve">
<value>Optimiza y convierte imágenes a cualquier formato al tiro.</value>
</data>
<data name="AboutSiteTitle" xml:space="preserve">
<value>Sobre Convert-It Online</value>
</data>
<data name="AboutSiteContent" xml:space="preserve">
<value>Convert-It Online es una plataforma gratuita que ofrece varias herramientas de conversión para que puedas hacer tu pega más fácil. Todas las conversiones son súper seguras, no guardamos tus archivos en nuestros servidores.</value>
</data>
<data name="WhyFreeTitle" xml:space="preserve">
<value>¿Por qué es gratis?</value>
</data>
<data name="WhyFreeContent" xml:space="preserve">
<value>Creemos que las herramientas útiles deberían estar al alcance de todos. Nuestro sitio se mantiene con publicidad no molesta, así puedes usar todos los conversores sin pagar ni una luca.</value>
</data>
<data name="SecurityTitle" xml:space="preserve">
<value>Seguridad y privacidad</value>
</data>
<data name="SecurityContent" xml:space="preserve">
<value>Tus archivos se procesan en tu navegador cuando se puede. Para conversiones que necesitan procesamiento en el servidor, los archivos se borran automáticamente después de la conversión.</value>
</data>
<!-- Navigation Menu - Chilean Terms -->
<data name="HomeLink" xml:space="preserve">
<value>Inicio</value>
</data>
<data name="TextMenuTitle" xml:space="preserve">
<value>Texto</value>
</data>
<data name="ImageMenuTitle" xml:space="preserve">
<value>Imagen</value>
</data>
<data name="CaseConverterTitle" xml:space="preserve">
<value>Conversor de Mayúsculas/Minúsculas</value>
</data>
<data name="JpgToWebpTitle" xml:space="preserve">
<value>JPG a WebP</value>
</data>
<!-- Footer -->
<data name="FooterText" xml:space="preserve">
<value>© 2025 Convert-It Online. Herramientas gratis de conversión.</value>
</data>
<data name="About" xml:space="preserve">
<value>Sobre nosotros</value>
</data>
<data name="Contact" xml:space="preserve">
<value>Contacto</value>
</data>
<data name="Terms" xml:space="preserve">
<value>Términos</value>
</data>
<!-- TextTools Page - Chilean -->
<data name="TextToolsPageTitle" xml:space="preserve">
<value>Conversor de Texto</value>
</data>
<data name="TextAreaLabel" xml:space="preserve">
<value>Pega tu texto acá</value>
</data>
<data name="ToUpperButton" xml:space="preserve">
<value>MAYÚSCULAS</value>
</data>
<data name="ToLowerButton" xml:space="preserve">
<value>minúsculas</value>
</data>
<data name="ToSentenceCaseButton" xml:space="preserve">
<value>Primera mayúscula</value>
</data>
<data name="ResultTitle" xml:space="preserve">
<value>Resultado</value>
</data>
<!-- Image Converter -->
<data name="ImageConverterPageTitle" xml:space="preserve">
<value>Conversor JPG a WebP</value>
</data>
<data name="ImageConverterPageDescription" xml:space="preserve">
<value>Convierte tus imágenes JPG al formato WebP al tiro y eficiente. WebP te da mejor compresión manteniendo la calidad de la imagen.</value>
</data>
<data name="FileInputLabel" xml:space="preserve">
<value>Selecciona un archivo JPG</value>
</data>
<data name="ConvertButton" xml:space="preserve">
<value>Convertir a WebP</value>
</data>
<!-- URL Translations -->
<data name="UrlTextTools" xml:space="preserve">
<value>herramientas-de-texto</value>
</data>
<data name="UrlImageConverters" xml:space="preserve">
<value>convertidores-de-imagen</value>
</data>
<data name="UrlCaseConverter" xml:space="preserve">
<value>conversor-de-mayusculas-minusculas</value>
</data>
<data name="UrlJpgToWebp" xml:space="preserve">
<value>jpg-a-webp</value>
</data>
</root>

122
SharedResource.es-MX.resx Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!-- Main Page Content - Mexican Spanish -->
<data name="PageTitle" xml:space="preserve">
<value>Convertidores Gratuitos en Línea</value>
</data>
<data name="Subtitle" xml:space="preserve">
<value>Convierte textos, imágenes y documentos gratis. Herramientas en línea rápidas y seguras, sin necesidad de instalar nada.</value>
</data>
<data name="ChooseConverter" xml:space="preserve">
<value>Elige tu Convertidor</value>
</data>
<data name="TextToolsTitle" xml:space="preserve">
<value>Herramientas de Texto</value>
</data>
<data name="TextToolsDescription" xml:space="preserve">
<value>Convierte, formatea y manipula textos fácilmente.</value>
</data>
<data name="ImageToolsTitle" xml:space="preserve">
<value>Convertidores de Imagen</value>
</data>
<data name="ImageToolsDescription" xml:space="preserve">
<value>Optimiza y convierte imágenes a cualquier formato.</value>
</data>
<data name="AboutSiteTitle" xml:space="preserve">
<value>Acerca de Convert-It Online</value>
</data>
<data name="AboutSiteContent" xml:space="preserve">
<value>Convert-It Online es una plataforma gratuita que ofrece diversas herramientas de conversión para facilitar tu trabajo diario. Todas las conversiones se realizan de forma segura, sin almacenar tus archivos en nuestros servidores.</value>
</data>
<data name="WhyFreeTitle" xml:space="preserve">
<value>¿Por qué es gratis?</value>
</data>
<data name="WhyFreeContent" xml:space="preserve">
<value>Creemos que las herramientas útiles deben ser accesibles para todos. Nuestro sitio se mantiene a través de anuncios no intrusivos, permitiendo que uses todos los convertidores sin costo alguno.</value>
</data>
<data name="SecurityTitle" xml:space="preserve">
<value>Seguridad y privacidad</value>
</data>
<data name="SecurityContent" xml:space="preserve">
<value>Tus archivos se procesan localmente en tu navegador siempre que sea posible. Para conversiones que requieren procesamiento en el servidor, los archivos se eliminan automáticamente después de la conversión.</value>
</data>
<!-- Navigation Menu - Mexican Terms -->
<data name="HomeLink" xml:space="preserve">
<value>Inicio</value>
</data>
<data name="TextMenuTitle" xml:space="preserve">
<value>Texto</value>
</data>
<data name="ImageMenuTitle" xml:space="preserve">
<value>Imagen</value>
</data>
<data name="CaseConverterTitle" xml:space="preserve">
<value>Convertidor de Mayúsculas/Minúsculas</value>
</data>
<data name="JpgToWebpTitle" xml:space="preserve">
<value>JPG a WebP</value>
</data>
<!-- Footer -->
<data name="FooterText" xml:space="preserve">
<value>© 2025 Convert-It Online. Herramientas gratuitas de conversión.</value>
</data>
<data name="About" xml:space="preserve">
<value>Acerca de</value>
</data>
<data name="Contact" xml:space="preserve">
<value>Contacto</value>
</data>
<data name="Terms" xml:space="preserve">
<value>Términos</value>
</data>
<!-- TextTools Page - Mexican -->
<data name="TextToolsPageTitle" xml:space="preserve">
<value>Convertidor de Texto</value>
</data>
<data name="TextAreaLabel" xml:space="preserve">
<value>Escribe tu texto aquí</value>
</data>
<data name="ToUpperButton" xml:space="preserve">
<value>MAYÚSCULAS</value>
</data>
<data name="ToLowerButton" xml:space="preserve">
<value>minúsculas</value>
</data>
<data name="ToSentenceCaseButton" xml:space="preserve">
<value>Primera mayúscula</value>
</data>
<data name="ResultTitle" xml:space="preserve">
<value>Resultado</value>
</data>
<!-- Image Converter -->
<data name="ImageConverterPageTitle" xml:space="preserve">
<value>Convertidor JPG a WebP</value>
</data>
<data name="ImageConverterPageDescription" xml:space="preserve">
<value>Convierte tus imágenes JPG al formato WebP de forma rápida y eficiente. WebP ofrece mejor compresión manteniendo la calidad de la imagen.</value>
</data>
<data name="FileInputLabel" xml:space="preserve">
<value>Selecciona un archivo JPG</value>
</data>
<data name="ConvertButton" xml:space="preserve">
<value>Convertir a WebP</value>
</data>
<!-- URL Translations -->
<data name="UrlTextTools" xml:space="preserve">
<value>herramientas-de-texto</value>
</data>
<data name="UrlImageConverters" xml:space="preserve">
<value>convertidores-de-imagen</value>
</data>
<data name="UrlCaseConverter" xml:space="preserve">
<value>conversor-de-mayusculas-minusculas</value>
</data>
<data name="UrlJpgToWebp" xml:space="preserve">
<value>jpg-a-webp</value>
</data>
</root>

122
SharedResource.es-PY.resx Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!-- Main Page Content - Paraguayan Spanish with Guaraní influences -->
<data name="PageTitle" xml:space="preserve">
<value>Convertidores Gratuitos en Línea</value>
</data>
<data name="Subtitle" xml:space="preserve">
<value>Convierte textos, imágenes y documentos gratis. Herramientas en línea rápidas y seguras, sin necesidad de instalar nada che.</value>
</data>
<data name="ChooseConverter" xml:space="preserve">
<value>Elige tu Convertidor</value>
</data>
<data name="TextToolsTitle" xml:space="preserve">
<value>Herramientas de Texto</value>
</data>
<data name="TextToolsDescription" xml:space="preserve">
<value>Convierte, formatea y manipula textos facilito.</value>
</data>
<data name="ImageToolsTitle" xml:space="preserve">
<value>Convertidores de Imagen</value>
</data>
<data name="ImageToolsDescription" xml:space="preserve">
<value>Optimiza y convierte imágenes a cualquier formato al toque.</value>
</data>
<data name="AboutSiteTitle" xml:space="preserve">
<value>Acerca de Convert-It Online</value>
</data>
<data name="AboutSiteContent" xml:space="preserve">
<value>Convert-It Online es una plataforma gratuita que ofrece diversas herramientas de conversión para facilitar tu trabajo diario che. Todas las conversiones se realizan de forma segura, sin guardar tus archivos en nuestros servidores.</value>
</data>
<data name="WhyFreeTitle" xml:space="preserve">
<value>¿Por qué es gratis?</value>
</data>
<data name="WhyFreeContent" xml:space="preserve">
<value>Creemos que las herramientas útiles deben estar al alcance de todos. Nuestro sitio se mantiene a través de publicidad no molesta, así podés usar todos los convertidores sin pagar nada che.</value>
</data>
<data name="SecurityTitle" xml:space="preserve">
<value>Seguridad y privacidad</value>
</data>
<data name="SecurityContent" xml:space="preserve">
<value>Tus archivos se procesan localmente en tu navegador cuando es posible. Para conversiones que necesitan procesamiento en el servidor, los archivos se borran automáticamente después de la conversión.</value>
</data>
<!-- Navigation Menu - Paraguayan Terms -->
<data name="HomeLink" xml:space="preserve">
<value>Inicio</value>
</data>
<data name="TextMenuTitle" xml:space="preserve">
<value>Texto</value>
</data>
<data name="ImageMenuTitle" xml:space="preserve">
<value>Imagen</value>
</data>
<data name="CaseConverterTitle" xml:space="preserve">
<value>Convertidor de Mayúsculas/Minúsculas</value>
</data>
<data name="JpgToWebpTitle" xml:space="preserve">
<value>JPG a WebP</value>
</data>
<!-- Footer -->
<data name="FooterText" xml:space="preserve">
<value>© 2025 Convert-It Online. Herramientas gratuitas de conversión.</value>
</data>
<data name="About" xml:space="preserve">
<value>Acerca de</value>
</data>
<data name="Contact" xml:space="preserve">
<value>Contacto</value>
</data>
<data name="Terms" xml:space="preserve">
<value>Términos</value>
</data>
<!-- TextTools Page - Paraguayan -->
<data name="TextToolsPageTitle" xml:space="preserve">
<value>Convertidor de Texto</value>
</data>
<data name="TextAreaLabel" xml:space="preserve">
<value>Escribí tu texto acá</value>
</data>
<data name="ToUpperButton" xml:space="preserve">
<value>MAYÚSCULAS</value>
</data>
<data name="ToLowerButton" xml:space="preserve">
<value>minúsculas</value>
</data>
<data name="ToSentenceCaseButton" xml:space="preserve">
<value>Primera mayúscula</value>
</data>
<data name="ResultTitle" xml:space="preserve">
<value>Resultado</value>
</data>
<!-- Image Converter -->
<data name="ImageConverterPageTitle" xml:space="preserve">
<value>Convertidor JPG a WebP</value>
</data>
<data name="ImageConverterPageDescription" xml:space="preserve">
<value>Convertí tus imágenes JPG al formato WebP de forma rápida y eficiente che. WebP ofrece mejor compresión manteniendo la calidad de la imagen.</value>
</data>
<data name="FileInputLabel" xml:space="preserve">
<value>Seleccioná un archivo JPG</value>
</data>
<data name="ConvertButton" xml:space="preserve">
<value>Convertir a WebP</value>
</data>
<!-- URL Translations -->
<data name="UrlTextTools" xml:space="preserve">
<value>herramientas-de-texto</value>
</data>
<data name="UrlImageConverters" xml:space="preserve">
<value>convertidores-de-imagen</value>
</data>
<data name="UrlCaseConverter" xml:space="preserve">
<value>conversor-de-mayusculas-minusculas</value>
</data>
<data name="UrlJpgToWebp" xml:space="preserve">
<value>jpg-a-webp</value>
</data>
</root>

View File

@ -204,6 +204,20 @@
<value>Termos</value>
</data>
<!-- URL Translations -->
<data name="UrlTextTools" xml:space="preserve">
<value>ferramentas-de-texto</value>
</data>
<data name="UrlImageConverters" xml:space="preserve">
<value>conversores-de-imagem</value>
</data>
<data name="UrlCaseConverter" xml:space="preserve">
<value>conversor-de-maiusculas-minusculas</value>
</data>
<data name="UrlJpgToWebp" xml:space="preserve">
<value>jpg-para-webp</value>
</data>
<!-- FAQ Resources for Contact Page -->
<data name="Faq1Question" xml:space="preserve">
<value>Como posso converter meus arquivos de forma segura?</value>

View File

@ -17,10 +17,7 @@
<div class="converter-menu">
@foreach (var tool in Model)
{
<a asp-area="@tool.Area"
asp-controller="@tool.Controller"
asp-action="@tool.Action"
asp-route-culture="@ViewContext.RouteData.Values["culture"]"
<a href="@Html.LocalizedUrl(tool.Area, tool.Controller, tool.Action)"
class="converter-item">
<div class="converter-icon">
<i class="@tool.IconClass"></i>

View File

@ -37,7 +37,7 @@
<i class="bi bi-fonts me-1"></i>@ViewBag.TextMenuTitle
</a>
<ul class="dropdown-menu" aria-labelledby="textToolsDropdown">
<li><a class="dropdown-item" asp-area="TextTools" asp-controller="CaseConverter" asp-action="Index" asp-route-culture="@requestCulture?.RequestCulture.UICulture.Name">
<li><a class="dropdown-item" href="@Html.LocalizedUrl("TextTools", "CaseConverter")">
<i class="bi bi-type me-2"></i>@ViewBag.CaseConverterTitle
</a></li>
</ul>
@ -47,7 +47,7 @@
<i class="bi bi-image-alt me-1"></i>@ViewBag.ImageMenuTitle
</a>
<ul class="dropdown-menu" aria-labelledby="imageToolsDropdown">
<li><a class="dropdown-item" asp-area="ImageConverters" asp-controller="JpgToWebp" asp-action="Index" asp-route-culture="@requestCulture?.RequestCulture.UICulture.Name">
<li><a class="dropdown-item" href="@Html.LocalizedUrl("ImageConverters", "JpgToWebp")">
<i class="bi bi-arrow-left-right me-2"></i>@ViewBag.JpgToWebpTitle
</a></li>
</ul>

View File

@ -1,4 +1,5 @@
@using Convert_It_Online
@using Convert_It_Online.Extensions
@using Microsoft.AspNetCore.Mvc.Localization
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers