mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 06:02:14 +03:00
fix(i18n): Turkish locale-aware search and sorting (#3115)
* feat(i18n): add Turkish locale-aware text helpers (search/sort) * test(i18n): cover null/whitespace edges + document compareTr/normalize contract * fix(i18n): route dashboard search through Turkish-safe matchesSearch * fix(i18n): sort user-visible lists with Turkish collation (compareTr) * fix(i18n): keep providerId tiebreaker as ASCII sort (technical id) * docs(i18n): document intentional lang=en in global-error boundary * chore(lint): guard against locale-unsafe toLowerCase().includes search * fix(i18n): migrate missed provider-name search + harden lint disable placement * fix(i18n): downgrade no-restricted-syntax to warn (incremental adoption) The rule errored on ~19 pre-existing toLowerCase().includes() call-sites in src/app accumulated since this PR's base. Keep it as a warning so the guard-rail guides future code without breaking the 0-errors lint gate (project policy: 0 errors, warnings tolerated). --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import path from "path";
|
||||
import { requireCliToolsAuth } from "@/lib/api/requireCliToolsAuth";
|
||||
import { ensureCliConfigWriteAllowed, getCliConfigPaths } from "@/shared/services/cliRuntime";
|
||||
import { resolveDataDir } from "@/lib/dataPaths";
|
||||
import { compareTr } from "@/shared/utils/turkishText";
|
||||
import { codexProfileIdSchema, codexProfileNameSchema } from "@/shared/validation/schemas";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
|
||||
@@ -88,7 +89,7 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
// Sort by name
|
||||
profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||
profiles.sort((a, b) => compareTr(a.name, b.name));
|
||||
return NextResponse.json({ profiles });
|
||||
} catch (error) {
|
||||
console.log("Error listing codex profiles:", error.message);
|
||||
|
||||
@@ -134,6 +134,7 @@ async function saveContinueConfig({ baseUrl, apiKey, model }) {
|
||||
normalizeApiBase(m.apiBase).includes("omniroute") ||
|
||||
normalizeApiBase(m.apiBase).includes(`localhost:${apiPort}`) ||
|
||||
normalizeApiBase(m.apiBase).includes(`127.0.0.1:${apiPort}`) ||
|
||||
// eslint-disable-next-line no-restricted-syntax -- teknik string kontrolü, kullanıcı metni araması değil
|
||||
String(m.apiKey || "")
|
||||
.toLowerCase()
|
||||
.includes("sk_omniroute"))
|
||||
|
||||
@@ -14,6 +14,7 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { readFileSync, existsSync } from "fs";
|
||||
import { getAppLogFilePath } from "@/lib/logEnv";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { matchesSearch } from "@/shared/utils/turkishText";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
const LEVEL_ORDER: Record<string, number> = {
|
||||
@@ -115,7 +116,7 @@ export async function GET(req: NextRequest) {
|
||||
// Filter by component
|
||||
if (componentFilter) {
|
||||
const comp = entry.component || entry.module || "";
|
||||
if (!comp.toLowerCase().includes(componentFilter.toLowerCase())) continue;
|
||||
if (!matchesSearch(comp, componentFilter)) continue;
|
||||
}
|
||||
|
||||
// Normalize timestamp field
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function GET(request: NextRequest) {
|
||||
value: `${m.provider}/${m.model}`,
|
||||
label: `${m.provider}/${m.model} - ${m.name}`,
|
||||
}))
|
||||
.sort((a, b) => a.value.localeCompare(b.value));
|
||||
.sort((a, b) => a.value.localeCompare(b.value)); // teknik sıralama: ASCII kasıtlı
|
||||
|
||||
// Add OpenRouter account models that explicitly support embeddings.
|
||||
try {
|
||||
@@ -85,7 +85,7 @@ export async function GET(request: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
options.sort((a, b) => a.value.localeCompare(b.value));
|
||||
options.sort((a, b) => a.value.localeCompare(b.value)); // teknik sıralama: ASCII kasıtlı
|
||||
|
||||
return NextResponse.json({ models: options });
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { skillRegistry } from "@/lib/skills/registry";
|
||||
import { parsePaginationParams, buildPaginatedResponse } from "@/shared/types/pagination";
|
||||
import { getSkillsProviderSetting } from "@/lib/skills/providerSettings";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { matchesSearch } from "@/shared/utils/turkishText";
|
||||
|
||||
const POPULAR_BY_PROVIDER = {
|
||||
skillsmp: ["web-search", "file-reader", "sql-assistant", "devops-helper", "docs-assistant"],
|
||||
@@ -18,7 +19,7 @@ export async function GET(request?: Request) {
|
||||
const provider = await getSkillsProviderSetting();
|
||||
const url = request?.url || "http://localhost/api/skills";
|
||||
const parsedUrl = new URL(url);
|
||||
const query = parsedUrl.searchParams.get("q")?.trim().toLowerCase() || "";
|
||||
const query = parsedUrl.searchParams.get("q")?.trim() || "";
|
||||
const modeFilter = parsedUrl.searchParams.get("mode");
|
||||
const sourceFilter = parsedUrl.searchParams.get("source");
|
||||
|
||||
@@ -26,11 +27,11 @@ export async function GET(request?: Request) {
|
||||
|
||||
if (query) {
|
||||
allSkills = allSkills.filter((skill) => {
|
||||
const tags = Array.isArray(skill.tags) ? skill.tags.join(" ").toLowerCase() : "";
|
||||
const tagsText = Array.isArray(skill.tags) ? skill.tags.join(" ") : "";
|
||||
return (
|
||||
skill.name.toLowerCase().includes(query) ||
|
||||
skill.description.toLowerCase().includes(query) ||
|
||||
tags.includes(query)
|
||||
matchesSearch(skill.name, query) ||
|
||||
matchesSearch(skill.description, query) ||
|
||||
matchesSearch(tagsText, query)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -462,6 +462,7 @@ export async function getUnifiedModelsResponse(
|
||||
? synced.attachment
|
||||
: syncedInputModalities.length > 0 || syncedOutputModalities.length > 0
|
||||
? [...syncedInputModalities, ...syncedOutputModalities].some((entry) =>
|
||||
// eslint-disable-next-line no-restricted-syntax -- teknik string kontrolü, kullanıcı metni araması değil
|
||||
entry.toLowerCase().includes("image")
|
||||
)
|
||||
: undefined;
|
||||
|
||||
Reference in New Issue
Block a user