mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-20 17:43:24 +00:00
Keep first 3 remote results pinned at the top (#4349)
This commit is contained in:
@@ -114,7 +114,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
|
||||
const { assistants } = useAI();
|
||||
const primaryAssistant = assistants[0];
|
||||
|
||||
if (error) {
|
||||
if (error && results.length === 0) {
|
||||
return (
|
||||
<output
|
||||
className={tcls(
|
||||
@@ -157,6 +157,26 @@ export const SearchResults = React.forwardRef(function SearchResults(
|
||||
</output>
|
||||
);
|
||||
|
||||
const partialError = error ? (
|
||||
<div
|
||||
className={tcls(
|
||||
'mt-3',
|
||||
'rounded-corners:rounded-md',
|
||||
'circular-corners:rounded-2xl',
|
||||
'bg-tint-subtle',
|
||||
'px-3',
|
||||
'py-2',
|
||||
'text-center',
|
||||
'text-sm',
|
||||
'text-tint-subtle',
|
||||
'animate-blur-in-slow'
|
||||
)}
|
||||
role="status"
|
||||
>
|
||||
{t(language, 'search_partial_error')}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<output className="flex grow flex-col" aria-busy={fetching}>
|
||||
{children}
|
||||
@@ -318,6 +338,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
|
||||
{!fetching && results.length === 0 ? noResults : null}
|
||||
</>
|
||||
)}
|
||||
{partialError}
|
||||
{fetching ? (
|
||||
<div
|
||||
className={tcls(
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { type MergedPageResult, getResultKey, reciprocalRankFusion } from './reciprocalRankFusion';
|
||||
import type { OrderedComputedResult } from './search-types';
|
||||
import type { LocalPageResult } from './useLocalSearchResults';
|
||||
|
||||
function localPage(id: string, title = id): LocalPageResult {
|
||||
return {
|
||||
type: 'local-page',
|
||||
id,
|
||||
title,
|
||||
pathname: `/${id}`,
|
||||
description: `Local description for ${title}`,
|
||||
breadcrumbs: [{ label: 'Local', icon: 'book-open' }],
|
||||
};
|
||||
}
|
||||
|
||||
function remotePage(id: string, title = id): OrderedComputedResult {
|
||||
return {
|
||||
type: 'page',
|
||||
id: `remote-${id}`,
|
||||
pageId: id,
|
||||
spaceId: 'space',
|
||||
title,
|
||||
href: `/${id}`,
|
||||
score: 0,
|
||||
breadcrumbs: [{ label: 'Remote' }],
|
||||
};
|
||||
}
|
||||
|
||||
function remoteRecord(id: string, title = id): OrderedComputedResult {
|
||||
return {
|
||||
type: 'record',
|
||||
id,
|
||||
title,
|
||||
href: `/records/${id}`,
|
||||
score: 0,
|
||||
description: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
describe('reciprocalRankFusion', () => {
|
||||
it('keeps the first 3 remote results first and in remote order', () => {
|
||||
const results = reciprocalRankFusion(
|
||||
[localPage('local-match', 'Alpha Local Match')],
|
||||
[
|
||||
remotePage('remote-1'),
|
||||
remoteRecord('remote-2'),
|
||||
remotePage('remote-3'),
|
||||
remotePage('remote-4'),
|
||||
],
|
||||
'alpha'
|
||||
);
|
||||
|
||||
expect(results.slice(0, 3).map(getResultKey)).toEqual([
|
||||
'page:remote-1',
|
||||
'record:remote-2',
|
||||
'page:remote-3',
|
||||
]);
|
||||
});
|
||||
|
||||
it('applies fusion only after the pinned remote results', () => {
|
||||
const results = reciprocalRankFusion(
|
||||
[localPage('local-match', 'Alpha Local Match')],
|
||||
[
|
||||
remotePage('remote-1'),
|
||||
remotePage('remote-2'),
|
||||
remotePage('remote-3'),
|
||||
remotePage('remote-4'),
|
||||
],
|
||||
'alpha'
|
||||
);
|
||||
|
||||
expect(results.slice(0, 3).map(getResultKey)).toEqual([
|
||||
'page:remote-1',
|
||||
'page:remote-2',
|
||||
'page:remote-3',
|
||||
]);
|
||||
const firstFusedResult = results[3];
|
||||
if (!firstFusedResult) {
|
||||
throw new Error('Expected a fused result after the pinned remote results');
|
||||
}
|
||||
expect(getResultKey(firstFusedResult)).toBe('page:local-match');
|
||||
});
|
||||
|
||||
it('pins all remote results when fewer than 3 are present', () => {
|
||||
const results = reciprocalRankFusion(
|
||||
[localPage('local-match', 'Alpha Local Match')],
|
||||
[remoteRecord('remote-1'), remotePage('remote-2')],
|
||||
'alpha'
|
||||
);
|
||||
|
||||
expect(results.map(getResultKey)).toEqual([
|
||||
'record:remote-1',
|
||||
'page:remote-2',
|
||||
'page:local-match',
|
||||
]);
|
||||
});
|
||||
|
||||
it('merges a pinned remote page with the matching local page without duplicating it', () => {
|
||||
const results = reciprocalRankFusion(
|
||||
[localPage('remote-1', 'Local title')],
|
||||
[
|
||||
remotePage('remote-1', 'Remote title'),
|
||||
remotePage('remote-2'),
|
||||
remotePage('remote-3'),
|
||||
],
|
||||
'remote'
|
||||
);
|
||||
const pinnedResult = results[0] as MergedPageResult;
|
||||
|
||||
expect(pinnedResult.type).toBe('page');
|
||||
expect(pinnedResult.title).toBe('Remote title');
|
||||
expect(pinnedResult.pathname).toBe('/remote-1');
|
||||
expect(pinnedResult.description).toBe('Local description for Local title');
|
||||
expect(pinnedResult.breadcrumbs).toEqual([{ label: 'Local', icon: 'book-open' }]);
|
||||
expect(results.map(getResultKey).filter((key) => key === 'page:remote-1')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not duplicate pinned records in the fused tail', () => {
|
||||
const results = reciprocalRankFusion(
|
||||
[],
|
||||
[
|
||||
remoteRecord('record-1'),
|
||||
remotePage('remote-2'),
|
||||
remotePage('remote-3'),
|
||||
remoteRecord('record-1'),
|
||||
remotePage('remote-4'),
|
||||
],
|
||||
'remote'
|
||||
);
|
||||
|
||||
expect(results.map(getResultKey).filter((key) => key === 'record:record-1')).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -66,6 +66,8 @@ function jaroWinkler(s1: string, s2: string, p = 0.1): number {
|
||||
const RRF_K = 60;
|
||||
/** Lower k for local results gives them a slightly higher score than remote ones at the same rank. */
|
||||
const RRF_K_LOCAL = 50;
|
||||
/** Number of remote results that should keep their API order before fusion. */
|
||||
const PINNED_REMOTE_RESULTS_COUNT = 3;
|
||||
|
||||
/**
|
||||
* A page result that was present in both local and remote lists.
|
||||
@@ -73,11 +75,12 @@ const RRF_K_LOCAL = 50;
|
||||
* and remote fields (href, pageId, spaceId, title) override them.
|
||||
* Breadcrumbs prefer local (has icon + emoji) and fall back to remote.
|
||||
*/
|
||||
export type MergedPageResult = ComputedPageResult & {
|
||||
export type MergedPageResult = Omit<ComputedPageResult, 'breadcrumbs'> & {
|
||||
pathname?: string;
|
||||
icon?: string;
|
||||
emoji?: string;
|
||||
description?: string;
|
||||
breadcrumbs?: LocalPageResult['breadcrumbs'] | ComputedPageResult['breadcrumbs'];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -100,16 +103,47 @@ export function getResultKey(
|
||||
|
||||
type RRFResult = LocalPageResult | OrderedComputedResult | MergedPageResult;
|
||||
|
||||
function mergeLocalPageWithRemotePage(
|
||||
localResult: LocalPageResult,
|
||||
remoteResult: ComputedPageResult
|
||||
): MergedPageResult {
|
||||
return {
|
||||
...localResult,
|
||||
...remoteResult,
|
||||
// Merge breadcrumbs: prefer local (has icon + emoji), fall back to remote.
|
||||
breadcrumbs: localResult.breadcrumbs ?? remoteResult.breadcrumbs,
|
||||
};
|
||||
}
|
||||
|
||||
function mergePinnedRemoteResult(
|
||||
result: OrderedComputedResult,
|
||||
localResultsByKey: Map<string, LocalPageResult>
|
||||
): RRFResult {
|
||||
if (result.type !== 'page') {
|
||||
return result;
|
||||
}
|
||||
|
||||
const localResult = localResultsByKey.get(getResultKey(result));
|
||||
if (!localResult) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mergeLocalPageWithRemotePage(localResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge local (FlexSearch) and remote (API) search results using
|
||||
* Reciprocal Rank Fusion (RRF).
|
||||
* Reciprocal Rank Fusion (RRF), while preserving the API order for the first
|
||||
* three remote results.
|
||||
*
|
||||
* RRF formula: score(d) = Σ_i 1 / (k + rank_i(d))
|
||||
*
|
||||
* Pages present in both lists are deep-merged: local fields act as the base
|
||||
* The pinned remote results are excluded from fusion and returned first. Pages
|
||||
* present in both lists are deep-merged: local fields act as the base
|
||||
* (preserving description, icon, emoji, pathname) and remote fields override
|
||||
* (providing href, pageId, spaceId, title). Breadcrumbs prefer local (has icon
|
||||
* + emoji) and fall back to remote. Their rank contributions from both lists are summed.
|
||||
* + emoji) and fall back to remote. In the fused tail, their rank contributions
|
||||
* from both lists are summed.
|
||||
*
|
||||
* Sections and records have no local equivalent and only accumulate their own
|
||||
* rank contribution.
|
||||
@@ -123,12 +157,22 @@ export function reciprocalRankFusion(
|
||||
remoteResults: OrderedComputedResult[],
|
||||
query: string
|
||||
): Array<RRFResult> {
|
||||
const localResultsByKey = new Map(localResults.map((result) => [getResultKey(result), result]));
|
||||
const pinnedRemoteResults = remoteResults.slice(0, PINNED_REMOTE_RESULTS_COUNT);
|
||||
const pinnedResultKeys = new Set(pinnedRemoteResults.map((result) => getResultKey(result)));
|
||||
const remainingLocalResults = localResults.filter(
|
||||
(result) => !pinnedResultKeys.has(getResultKey(result))
|
||||
);
|
||||
const remainingRemoteResults = remoteResults
|
||||
.slice(PINNED_REMOTE_RESULTS_COUNT)
|
||||
.filter((result) => !pinnedResultKeys.has(getResultKey(result)));
|
||||
|
||||
// Map from dedup key → { result, score }
|
||||
const scoreMap = new Map<string, { result: RRFResult; score: number }>();
|
||||
|
||||
// Process local results first (1-indexed rank)
|
||||
// Using RRF_K_LOCAL (< RRF_K) slightly boosts local scores over remote ones.
|
||||
localResults.forEach((result, index) => {
|
||||
remainingLocalResults.forEach((result, index) => {
|
||||
const rank = index + 1;
|
||||
const key = getResultKey(result);
|
||||
const contribution = 1 / (RRF_K_LOCAL + rank);
|
||||
@@ -142,7 +186,7 @@ export function reciprocalRankFusion(
|
||||
});
|
||||
|
||||
// Process remote results, deduplicating against local pages
|
||||
remoteResults.forEach((result, index) => {
|
||||
remainingRemoteResults.forEach((result, index) => {
|
||||
const rank = index + 1;
|
||||
const contribution = 1 / (RRF_K + rank);
|
||||
|
||||
@@ -155,12 +199,7 @@ export function reciprocalRankFusion(
|
||||
// (href, pageId, spaceId, breadcrumbs, title).
|
||||
existing.score += contribution;
|
||||
if (existing.result.type === 'local-page' && result.type === 'page') {
|
||||
existing.result = {
|
||||
...existing.result,
|
||||
...result,
|
||||
// Merge breadcrumbs: prefer local (has icon + emoji), fall back to remote.
|
||||
breadcrumbs: existing.result.breadcrumbs ?? result.breadcrumbs,
|
||||
} as MergedPageResult;
|
||||
existing.result = mergeLocalPageWithRemotePage(existing.result, result);
|
||||
} else {
|
||||
existing.result = result;
|
||||
}
|
||||
@@ -197,7 +236,12 @@ export function reciprocalRankFusion(
|
||||
}
|
||||
|
||||
// Sort descending by RRF score
|
||||
return Array.from(scoreMap.values())
|
||||
const fusedResults = Array.from(scoreMap.values())
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map(({ result }) => result);
|
||||
|
||||
return [
|
||||
...pinnedRemoteResults.map((result) => mergePinnedRemoteResult(result, localResultsByKey)),
|
||||
...fusedResults,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export const ar: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'تعذر العثور على إجابة لسؤالك. يمكنك محاولة إعادة صياغته أو جعله أكثر تحديدا.',
|
||||
search_ask_error: 'حدث خطأ ما. يرجى المحاولة مرة أخرى لاحقا.',
|
||||
search_partial_error: 'تعذر تحميل بعض النتائج. يتم عرض النتائج المتاحة.',
|
||||
on_this_page: 'في هذه الصفحة',
|
||||
next_page: 'التالي',
|
||||
previous_page: 'السابق',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const bg: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Не беше намерен отговор на въпроса ви. Можете да опитате да го преформулирате или да бъдете по-конкретни.',
|
||||
search_ask_error: 'Нещо се обърка. Моля, опитайте отново по-късно.',
|
||||
search_partial_error:
|
||||
'Някои резултати не можаха да се заредят. Показват се наличните резултати.',
|
||||
on_this_page: 'На тази страница',
|
||||
next_page: 'Следваща',
|
||||
previous_page: 'Предишна',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const cs: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Na vaši otázku se nepodařilo najít odpověď. Zkuste ji přeformulovat nebo být konkrétnější.',
|
||||
search_ask_error: 'Něco se pokazilo. Zkuste to prosím znovu později.',
|
||||
search_partial_error: 'Některé výsledky se nepodařilo načíst. Zobrazují se dostupné výsledky.',
|
||||
on_this_page: 'Na této stránce',
|
||||
next_page: 'Další',
|
||||
previous_page: 'Předchozí',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const da: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Der kunne ikke findes et svar på dit spørgsmål. Prøv at omformulere det eller være mere specifik.',
|
||||
search_ask_error: 'Noget gik galt. Prøv igen senere.',
|
||||
search_partial_error: 'Nogle resultater kunne ikke indlæses. Viser tilgængelige resultater.',
|
||||
on_this_page: 'På denne side',
|
||||
next_page: 'Næste',
|
||||
previous_page: 'Forrige',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const de = {
|
||||
import type { TranslationLanguage } from './types';
|
||||
|
||||
export const de: TranslationLanguage = {
|
||||
locale: 'de',
|
||||
language: 'Deutsch',
|
||||
flag: '🇩🇪',
|
||||
@@ -39,6 +41,8 @@ export const de = {
|
||||
search_ask_no_answer:
|
||||
'Es konnte keine Antwort auf Ihre Frage gefunden werden. Versuchen Sie, sie umzuformulieren oder genauer zu sein.',
|
||||
search_ask_error: 'Etwas ist schief gelaufen. Bitte versuchen Sie es später noch einmal.',
|
||||
search_partial_error:
|
||||
'Einige Ergebnisse konnten nicht geladen werden. Verfügbare Ergebnisse werden angezeigt.',
|
||||
on_this_page: 'Auf dieser Seite',
|
||||
next_page: 'Nächste',
|
||||
previous_page: 'Vorherige',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const el: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Δεν ήταν δυνατή η εύρεση απάντησης στην ερώτησή σας. Δοκιμάστε να τη διατυπώσετε διαφορετικά ή πιο συγκεκριμένα.',
|
||||
search_ask_error: 'Κάτι πήγε στραβά. Δοκιμάστε ξανά αργότερα.',
|
||||
search_partial_error:
|
||||
'Δεν ήταν δυνατή η φόρτωση ορισμένων αποτελεσμάτων. Εμφανίζονται τα διαθέσιμα αποτελέσματα.',
|
||||
on_this_page: 'Σε αυτήν τη σελίδα',
|
||||
next_page: 'Επόμενη',
|
||||
previous_page: 'Προηγούμενη',
|
||||
|
||||
@@ -38,6 +38,7 @@ export const en = {
|
||||
search_ask_no_answer:
|
||||
'An answer could not be found for your question. You could try rephrasing it, or be more specific.',
|
||||
search_ask_error: 'Something went wrong. Please try again later.',
|
||||
search_partial_error: "Some results couldn't load. Showing available results.",
|
||||
on_this_page: 'On this page',
|
||||
next_page: 'Next',
|
||||
previous_page: 'Previous',
|
||||
|
||||
@@ -41,6 +41,8 @@ export const es: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'No se pudo encontrar una respuesta para su pregunta. Puede intentar reformularla o ser más específico.',
|
||||
search_ask_error: 'Algo salió mal. Por favor, inténtalo de nuevo más tarde.',
|
||||
search_partial_error:
|
||||
'No se pudieron cargar algunos resultados. Se muestran los resultados disponibles.',
|
||||
on_this_page: 'En esta página',
|
||||
next_page: 'Siguiente',
|
||||
previous_page: 'Anterior',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const et: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Teie küsimusele ei leitud vastust. Proovige seda ümber sõnastada või olla täpsem.',
|
||||
search_ask_error: 'Midagi läks valesti. Palun proovige hiljem uuesti.',
|
||||
search_partial_error: 'Mõnda tulemust ei saanud laadida. Kuvatakse saadaolevad tulemused.',
|
||||
on_this_page: 'Sellel lehel',
|
||||
next_page: 'Järgmine',
|
||||
previous_page: 'Eelmine',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const fi: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Kysymykseesi ei löytynyt vastausta. Voit yrittää muotoilla sen uudelleen tai olla tarkempi.',
|
||||
search_ask_error: 'Jokin meni pieleen. Yritä myöhemmin uudelleen.',
|
||||
search_partial_error:
|
||||
'Joitakin tuloksia ei voitu ladata. Näytetään saatavilla olevat tulokset.',
|
||||
on_this_page: 'Tällä sivulla',
|
||||
next_page: 'Seuraava',
|
||||
previous_page: 'Edellinen',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const fr = {
|
||||
import type { TranslationLanguage } from './types';
|
||||
|
||||
export const fr: TranslationLanguage = {
|
||||
locale: 'fr',
|
||||
language: 'Français',
|
||||
flag: '🇫🇷',
|
||||
@@ -38,6 +40,8 @@ export const fr = {
|
||||
search_ask_sources_no_answer: 'Pages associées',
|
||||
search_ask_no_answer: 'Pas de réponse trouvée. Essayez de reformuler votre question.',
|
||||
search_ask_error: 'Une erreur est survenue. Veuillez réessayer plus tard.',
|
||||
search_partial_error:
|
||||
'Certains résultats n’ont pas pu être chargés. Les résultats disponibles sont affichés.',
|
||||
on_this_page: 'Sur cette page',
|
||||
next_page: 'Suivant',
|
||||
previous_page: 'Précédent',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const he: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'לא נמצאה תשובה לשאלה שלך. אפשר לנסות לנסח אותה מחדש או להיות מדויקים יותר.',
|
||||
search_ask_error: 'משהו השתבש. נסה שוב מאוחר יותר.',
|
||||
search_partial_error: 'לא ניתן היה לטעון חלק מהתוצאות. מוצגות התוצאות הזמינות.',
|
||||
on_this_page: 'בדף הזה',
|
||||
next_page: 'הבא',
|
||||
previous_page: 'הקודם',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const hi: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'आपके प्रश्न का उत्तर नहीं मिल सका। आप इसे फिर से लिखने या अधिक विशिष्ट बनाने की कोशिश कर सकते हैं।',
|
||||
search_ask_error: 'कुछ गलत हो गया। कृपया बाद में फिर कोशिश करें।',
|
||||
search_partial_error: 'कुछ परिणाम लोड नहीं हो सके। उपलब्ध परिणाम दिखाए जा रहे हैं।',
|
||||
on_this_page: 'इस पृष्ठ पर',
|
||||
next_page: 'अगला',
|
||||
previous_page: 'पिछला',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const hr: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Nije bilo moguće pronaći odgovor na vaše pitanje. Pokušajte ga preformulirati ili biti konkretniji.',
|
||||
search_ask_error: 'Nešto je pošlo po zlu. Pokušajte ponovno kasnije.',
|
||||
search_partial_error: 'Neki se rezultati nisu mogli učitati. Prikazuju se dostupni rezultati.',
|
||||
on_this_page: 'Na ovoj stranici',
|
||||
next_page: 'Sljedeća',
|
||||
previous_page: 'Prethodna',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const hu: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Nem található válasz a kérdésére. Próbálja meg átfogalmazni, vagy legyen pontosabb.',
|
||||
search_ask_error: 'Valami hiba történt. Kérjük, próbálja újra később.',
|
||||
search_partial_error:
|
||||
'Néhány találatot nem sikerült betölteni. Az elérhető találatok jelennek meg.',
|
||||
on_this_page: 'Ezen az oldalon',
|
||||
next_page: 'Következő',
|
||||
previous_page: 'Előző',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const id: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Jawaban untuk pertanyaan Anda tidak dapat ditemukan. Anda dapat mencoba menyusun ulang atau membuatnya lebih spesifik.',
|
||||
search_ask_error: 'Terjadi kesalahan. Silakan coba lagi nanti.',
|
||||
search_partial_error: 'Beberapa hasil tidak dapat dimuat. Menampilkan hasil yang tersedia.',
|
||||
on_this_page: 'Di halaman ini',
|
||||
next_page: 'Berikutnya',
|
||||
previous_page: 'Sebelumnya',
|
||||
|
||||
@@ -41,6 +41,8 @@ export const it: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Non è stato possibile trovare una risposta alla tua domanda. Prova a riformularla o a essere più specifico.',
|
||||
search_ask_error: 'Qualcosa è andato storto. Riprova più tardi.',
|
||||
search_partial_error:
|
||||
'Non è stato possibile caricare alcuni risultati. Visualizziamo i risultati disponibili.',
|
||||
on_this_page: 'In questa pagina',
|
||||
next_page: 'Successivo',
|
||||
previous_page: 'Precedente',
|
||||
|
||||
@@ -41,6 +41,7 @@ export const ja: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'ご質問への回答が見つかりませんでした。質問を言い換えるか、もう少し具体的にしてください。',
|
||||
search_ask_error: '何らかのエラーが発生しました。後ほど再度お試しください。',
|
||||
search_partial_error: '一部の結果を読み込めませんでした。利用可能な結果を表示しています。',
|
||||
on_this_page: 'このページ内',
|
||||
next_page: '次へ',
|
||||
previous_page: '前へ',
|
||||
|
||||
@@ -41,6 +41,7 @@ export const ko: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'질문에 대한 답변을 찾을 수 없습니다. 질문을 다시 표현하거나 더 구체적으로 작성해 보세요.',
|
||||
search_ask_error: '문제가 발생했습니다. 나중에 다시 시도해 주세요.',
|
||||
search_partial_error: '일부 결과를 불러오지 못했습니다. 사용 가능한 결과를 표시합니다.',
|
||||
on_this_page: '이 페이지에서',
|
||||
next_page: '다음',
|
||||
previous_page: '이전',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const lt: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Nepavyko rasti atsakymo į jūsų klausimą. Galite pabandyti jį performuluoti arba pateikti konkrečiau.',
|
||||
search_ask_error: 'Kažkas nepavyko. Bandykite dar kartą vėliau.',
|
||||
search_partial_error: 'Nepavyko įkelti kai kurių rezultatų. Rodomi pasiekiami rezultatai.',
|
||||
on_this_page: 'Šiame puslapyje',
|
||||
next_page: 'Kitas',
|
||||
previous_page: 'Ankstesnis',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const lv: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Neizdevās atrast atbildi uz jūsu jautājumu. Varat mēģināt to pārfrāzēt vai būt konkrētāks.',
|
||||
search_ask_error: 'Kaut kas nogāja greizi. Lūdzu, mēģiniet vēlāk vēlreiz.',
|
||||
search_partial_error: 'Dažus rezultātus neizdevās ielādēt. Tiek rādīti pieejamie rezultāti.',
|
||||
on_this_page: 'Šajā lapā',
|
||||
next_page: 'Nākamā',
|
||||
previous_page: 'Iepriekšējā',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const ms: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Jawapan untuk soalan anda tidak ditemui. Anda boleh cuba menyusunnya semula atau bertanya dengan lebih khusus.',
|
||||
search_ask_error: 'Ada sesuatu yang tidak kena. Sila cuba lagi kemudian.',
|
||||
search_partial_error: 'Sesetengah hasil tidak dapat dimuatkan. Memaparkan hasil yang tersedia.',
|
||||
on_this_page: 'Pada halaman ini',
|
||||
next_page: 'Seterusnya',
|
||||
previous_page: 'Sebelumnya',
|
||||
|
||||
@@ -41,6 +41,8 @@ export const nl: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Er kon geen antwoord op je vraag worden gevonden. Probeer je vraag anders te formuleren of wees specifieker.',
|
||||
search_ask_error: 'Er is iets misgegaan. Probeer het later opnieuw.',
|
||||
search_partial_error:
|
||||
'Sommige resultaten konden niet worden geladen. Beschikbare resultaten worden weergegeven.',
|
||||
on_this_page: 'Op deze pagina',
|
||||
next_page: 'Volgende',
|
||||
previous_page: 'Vorige',
|
||||
|
||||
@@ -41,6 +41,7 @@ export const no: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Et svar på spørsmålet ditt kunne ikke finnes. Prøv å omformulere det eller være mer spesifikk.',
|
||||
search_ask_error: 'Noe gikk galt. Vennligst prøv igjen senere.',
|
||||
search_partial_error: 'Noen resultater kunne ikke lastes inn. Viser tilgjengelige resultater.',
|
||||
on_this_page: 'På denne siden',
|
||||
next_page: 'Neste',
|
||||
previous_page: 'Forrige',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const pl: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Nie udało się znaleźć odpowiedzi na Twoje pytanie. Spróbuj sformułować je inaczej lub bardziej szczegółowo.',
|
||||
search_ask_error: 'Coś poszło nie tak. Spróbuj ponownie później.',
|
||||
search_partial_error: 'Nie udało się wczytać niektórych wyników. Wyświetlamy dostępne wyniki.',
|
||||
on_this_page: 'Na tej stronie',
|
||||
next_page: 'Dalej',
|
||||
previous_page: 'Wstecz',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const pt_br = {
|
||||
import type { TranslationLanguage } from './types';
|
||||
|
||||
export const pt_br: TranslationLanguage = {
|
||||
locale: 'pt-br',
|
||||
language: 'Português (Brasil)',
|
||||
flag: '🇧🇷',
|
||||
@@ -39,6 +41,8 @@ export const pt_br = {
|
||||
search_ask_no_answer:
|
||||
'Não foi possível encontrar uma resposta para sua pergunta. Você pode tentar reformulá-la ou ser mais específico.',
|
||||
search_ask_error: 'Algo deu errado. Por favor, tente novamente mais tarde.',
|
||||
search_partial_error:
|
||||
'Alguns resultados não puderam ser carregados. Mostrando os resultados disponíveis.',
|
||||
on_this_page: 'Nesta página',
|
||||
next_page: 'Próximo',
|
||||
previous_page: 'Anterior',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const pt: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Não foi possível encontrar uma resposta para a sua pergunta. Pode tentar reformulá-la ou ser mais específico.',
|
||||
search_ask_error: 'Algo correu mal. Tente novamente mais tarde.',
|
||||
search_partial_error:
|
||||
'Não foi possível carregar alguns resultados. A mostrar os resultados disponíveis.',
|
||||
on_this_page: 'Nesta página',
|
||||
next_page: 'Seguinte',
|
||||
previous_page: 'Anterior',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const ro: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Nu s-a putut găsi un răspuns la întrebarea ta. Poți încerca să o reformulezi sau să fii mai specific.',
|
||||
search_ask_error: 'Ceva nu a mers bine. Te rugăm să încerci din nou mai târziu.',
|
||||
search_partial_error:
|
||||
'Unele rezultate nu au putut fi încărcate. Se afișează rezultatele disponibile.',
|
||||
on_this_page: 'Pe această pagină',
|
||||
next_page: 'Următoarea',
|
||||
previous_page: 'Anterioara',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const ru = {
|
||||
import type { TranslationLanguage } from './types';
|
||||
|
||||
export const ru: TranslationLanguage = {
|
||||
locale: 'ru',
|
||||
language: 'Русский',
|
||||
flag: '🇷🇺',
|
||||
@@ -39,6 +41,8 @@ export const ru = {
|
||||
search_ask_no_answer:
|
||||
'Не удалось найти ответ на ваш вопрос. Попробуйте перефразировать его или задать более конкретный вопрос.',
|
||||
search_ask_error: 'Что-то пошло не так. Пожалуйста, попробуйте позже.',
|
||||
search_partial_error:
|
||||
'Не удалось загрузить некоторые результаты. Показаны доступные результаты.',
|
||||
on_this_page: 'На этой странице',
|
||||
next_page: 'Следующая',
|
||||
previous_page: 'Предыдущая',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const sk: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Na vašu otázku sa nepodarilo nájsť odpoveď. Skúste ju preformulovať alebo byť konkrétnejší.',
|
||||
search_ask_error: 'Niečo sa pokazilo. Skúste to znova neskôr.',
|
||||
search_partial_error:
|
||||
'Niektoré výsledky sa nepodarilo načítať. Zobrazujú sa dostupné výsledky.',
|
||||
on_this_page: 'Na tejto stránke',
|
||||
next_page: 'Ďalej',
|
||||
previous_page: 'Predchádzajúca',
|
||||
|
||||
@@ -40,6 +40,8 @@ export const sl: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Odgovora na vaše vprašanje ni bilo mogoče najti. Poskusite ga preoblikovati ali biti bolj natančni.',
|
||||
search_ask_error: 'Nekaj je šlo narobe. Poskusite znova pozneje.',
|
||||
search_partial_error:
|
||||
'Nekaterih rezultatov ni bilo mogoče naložiti. Prikazani so razpoložljivi rezultati.',
|
||||
on_this_page: 'Na tej strani',
|
||||
next_page: 'Naslednja',
|
||||
previous_page: 'Prejšnja',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const sv: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Det gick inte att hitta ett svar på din fråga. Du kan försöka formulera om den eller vara mer specifik.',
|
||||
search_ask_error: 'Något gick fel. Försök igen senare.',
|
||||
search_partial_error: 'Vissa resultat kunde inte läsas in. Visar tillgängliga resultat.',
|
||||
on_this_page: 'På den här sidan',
|
||||
next_page: 'Nästa',
|
||||
previous_page: 'Föregående',
|
||||
|
||||
@@ -39,6 +39,7 @@ export const th: TranslationLanguage = {
|
||||
search_ask_sources_no_answer: 'หน้าที่เกี่ยวข้อง',
|
||||
search_ask_no_answer: 'ไม่พบคำตอบสำหรับคำถามของคุณ ลองเรียบเรียงใหม่หรือถามให้เฉพาะเจาะจงขึ้น',
|
||||
search_ask_error: 'มีบางอย่างผิดพลาด โปรดลองอีกครั้งในภายหลัง',
|
||||
search_partial_error: 'โหลดผลลัพธ์บางรายการไม่ได้ กำลังแสดงผลลัพธ์ที่พร้อมใช้งาน',
|
||||
on_this_page: 'ในหน้านี้',
|
||||
next_page: 'ถัดไป',
|
||||
previous_page: 'ก่อนหน้า',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const tr: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Sorunuz için bir cevap bulunamadı. Soruyu yeniden ifade etmeyi veya daha spesifik olmayı deneyebilirsiniz.',
|
||||
search_ask_error: 'Bir şeyler ters gitti. Lütfen daha sonra tekrar deneyin.',
|
||||
search_partial_error: 'Bazı sonuçlar yüklenemedi. Kullanılabilir sonuçlar gösteriliyor.',
|
||||
on_this_page: 'Bu sayfada',
|
||||
next_page: 'Sonraki',
|
||||
previous_page: 'Önceki',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const uk: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Не вдалося знайти відповідь на ваше запитання. Спробуйте переформулювати його або зробити конкретнішим.',
|
||||
search_ask_error: 'Щось пішло не так. Спробуйте ще раз пізніше.',
|
||||
search_partial_error: 'Не вдалося завантажити деякі результати. Показано доступні результати.',
|
||||
on_this_page: 'На цій сторінці',
|
||||
next_page: 'Наступна',
|
||||
previous_page: 'Попередня',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const vi: TranslationLanguage = {
|
||||
search_ask_no_answer:
|
||||
'Không tìm thấy câu trả lời cho câu hỏi của bạn. Bạn có thể thử diễn đạt lại hoặc hỏi cụ thể hơn.',
|
||||
search_ask_error: 'Đã xảy ra lỗi. Vui lòng thử lại sau.',
|
||||
search_partial_error: 'Không thể tải một số kết quả. Đang hiển thị các kết quả có sẵn.',
|
||||
on_this_page: 'Trên trang này',
|
||||
next_page: 'Tiếp theo',
|
||||
previous_page: 'Trước',
|
||||
|
||||
@@ -39,6 +39,7 @@ export const yue: TranslationLanguage = {
|
||||
search_ask_sources_no_answer: '相關頁面',
|
||||
search_ask_no_answer: '搵唔到你問題嘅答案。你可以試下改寫,或者問得更具體。',
|
||||
search_ask_error: '發生錯誤。請稍後再試。',
|
||||
search_partial_error: '部分結果載入唔到。顯示緊可用嘅結果。',
|
||||
on_this_page: '本頁內容',
|
||||
next_page: '下一頁',
|
||||
previous_page: '上一頁',
|
||||
|
||||
@@ -39,6 +39,7 @@ export const zh_tw: TranslationLanguage = {
|
||||
search_ask_sources_no_answer: '相關頁面',
|
||||
search_ask_no_answer: '找不到您問題的答案。您可以嘗試改寫問題,或提出更具體的內容。',
|
||||
search_ask_error: '發生錯誤。請稍後再試。',
|
||||
search_partial_error: '部分結果無法載入。正在顯示可用結果。',
|
||||
on_this_page: '本頁內容',
|
||||
next_page: '下一頁',
|
||||
previous_page: '上一頁',
|
||||
|
||||
@@ -40,6 +40,7 @@ export const zh: TranslationLanguage = {
|
||||
search_ask_sources_no_answer: '相关页面',
|
||||
search_ask_no_answer: '无法找到您的问题的答案。您可以尝试改述问题或提供更具体的信息。',
|
||||
search_ask_error: '出了些问题。请稍后再试。',
|
||||
search_partial_error: '部分结果无法加载。正在显示可用结果。',
|
||||
on_this_page: '在本页',
|
||||
next_page: '下一页',
|
||||
previous_page: '上一页',
|
||||
|
||||
Reference in New Issue
Block a user