Multilingual support for Algolia search

This commit is contained in:
Mimi 2020-08-19 00:23:45 +08:00
parent 15a21eeca7
commit 60facf4b00
8 changed files with 38 additions and 28 deletions

View File

@ -725,10 +725,6 @@ algolia_search:
enable: false enable: false
hits: hits:
per_page: 10 per_page: 10
labels:
input_placeholder: Search for Posts
hits_empty: "We didn't find any results for the search: ${query}"
hits_stats: "${hits} results found in ${time} ms"
# Local Search # Local Search
# Dependencies: https://github.com/next-theme/hexo-generator-searchdb # Dependencies: https://github.com/next-theme/hexo-generator-searchdb

View File

@ -67,6 +67,9 @@ state:
search: search:
placeholder: Searching... placeholder: Searching...
empty: "We didn't find any results for the search: %s"
hits_time: "%s results found in %s ms"
hits: "%s results found"
cheers: cheers:
um: Um.. um: Um..

View File

@ -12,7 +12,7 @@
</span> </span>
</div> </div>
<div class="search-result-container"> <div class="search-result-container">
<div class="no-result"> <div class="search-result-icon">
<i class="fa fa-spinner fa-pulse fa-5x"></i> <i class="fa fa-spinner fa-pulse fa-5x"></i>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ const { parse } = require('url');
* Export theme config to js * Export theme config to js
*/ */
hexo.extend.helper.register('next_config', function() { hexo.extend.helper.register('next_config', function() {
const { config, theme, next_version } = this; const { config, theme, next_version, __ } = this;
const exportConfig = { const exportConfig = {
hostname : parse(config.url).hostname || config.url, hostname : parse(config.url).hostname || config.url,
root : config.root, root : config.root,
@ -24,15 +24,20 @@ hexo.extend.helper.register('next_config', function() {
pangu : theme.pangu, pangu : theme.pangu,
comments : theme.comments, comments : theme.comments,
motion : theme.motion, motion : theme.motion,
prism : config.prismjs.enable && !config.prismjs.preprocess prism : config.prismjs.enable && !config.prismjs.preprocess,
i18n : {
placeholder: __('search.placeholder'),
empty : __('search.empty', '${query}'),
hits_time : __('search.hits_time', '${hits}', '${time}'),
hits : __('search.hits', '${hits}')
}
}; };
if (config.algolia && theme.algolia_search && theme.algolia_search.enable) { if (config.algolia && theme.algolia_search && theme.algolia_search.enable) {
exportConfig.algolia = { exportConfig.algolia = {
appID : config.algolia.applicationID || config.algolia.appId, appID : config.algolia.applicationID || config.algolia.appId,
apiKey : config.algolia.apiKey, apiKey : config.algolia.apiKey,
indexName: config.algolia.indexName, indexName: config.algolia.indexName,
hits : theme.algolia_search.hits, hits : theme.algolia_search.hits
labels : theme.algolia_search.labels
}; };
} }
if (config.search && theme.local_search && theme.local_search.enable) { if (config.search && theme.local_search && theme.local_search.enable) {

View File

@ -102,7 +102,7 @@
height: 100%; height: 100%;
margin-left: -2px; margin-left: -2px;
position: absolute; position: absolute;
// 1.25em is inaccurate when .collection-title has line breaks on mobile // To do: 1.25em is inaccurate when .collection-title has line breaks on mobile
top: 1.25em; top: 1.25em;
width: 4px; width: 4px;
} }

View File

@ -114,11 +114,11 @@ if (hexo-config('algolia_search.enable')) {
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
}
.algolia-powered { img {
height: 1em; height: 1em;
margin: 0; margin: 0;
}
} }
.algolia-pagination { .algolia-pagination {
@ -149,7 +149,7 @@ if (hexo-config('local_search.enable')) {
padding: 2px; padding: 2px;
} }
.search-result-container { .no-result {
display: flex; display: flex;
} }
@ -157,7 +157,7 @@ if (hexo-config('local_search.enable')) {
width: 100%; width: 100%;
} }
.no-result { .search-result-icon {
color: $grey-light; color: $grey-light;
margin: auto; margin: auto;
} }

View File

@ -1,8 +1,7 @@
/* global instantsearch, algoliasearch, CONFIG */ /* global instantsearch, algoliasearch, CONFIG */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const algoliaSettings = CONFIG.algolia; const { indexName, appID, apiKey, hits } = CONFIG.algolia;
const { indexName, appID, apiKey } = algoliaSettings;
const search = instantsearch({ const search = instantsearch({
indexName, indexName,
@ -21,12 +20,12 @@ document.addEventListener('DOMContentLoaded', () => {
// Registering Widgets // Registering Widgets
search.addWidgets([ search.addWidgets([
instantsearch.widgets.configure({ instantsearch.widgets.configure({
hitsPerPage: algoliaSettings.hits.per_page || 10 hitsPerPage: hits.per_page || 10
}), }),
instantsearch.widgets.searchBox({ instantsearch.widgets.searchBox({
container : '.search-input-container', container : '.search-input-container',
placeholder : algoliaSettings.labels.input_placeholder, placeholder : CONFIG.i18n.placeholder,
// Hide default icons of algolia search // Hide default icons of algolia search
showReset : false, showReset : false,
showSubmit : false, showSubmit : false,
@ -40,11 +39,11 @@ document.addEventListener('DOMContentLoaded', () => {
container: '.algolia-stats', container: '.algolia-stats',
templates: { templates: {
text: data => { text: data => {
const stats = algoliaSettings.labels.hits_stats const stats = CONFIG.i18n.hits_time
.replace(/\$\{hits}/, data.nbHits) .replace(/\$\{hits}/, data.nbHits)
.replace(/\$\{time}/, data.processingTimeMS); .replace(/\$\{time}/, data.processingTimeMS);
return `<span>${stats}</span> return `<span>${stats}</span>
<img src="${CONFIG.root}images/logo-algolia-nebula-blue-full.svg" class="algolia-powered" alt="Algolia">`; <img src="${CONFIG.root}images/logo-algolia-nebula-blue-full.svg" alt="Algolia">`;
} }
}, },
cssClasses: { cssClasses: {
@ -69,7 +68,7 @@ document.addEventListener('DOMContentLoaded', () => {
}, },
empty: data => { empty: data => {
return `<div id="algolia-hits-empty"> return `<div id="algolia-hits-empty">
${algoliaSettings.labels.hits_empty.replace(/\$\{query}/, data.query)} ${CONFIG.i18n.empty.replace(/\$\{query}/, data.query)}
</div>`; </div>`;
} }
}, },

View File

@ -164,16 +164,18 @@ document.addEventListener('DOMContentLoaded', () => {
if (!isfetched) return; if (!isfetched) return;
const searchText = input.value.trim().toLowerCase(); const searchText = input.value.trim().toLowerCase();
const keywords = searchText.split(/[-\s]+/); const keywords = searchText.split(/[-\s]+/);
const resultContent = document.querySelector('.search-result-container'); const container = document.querySelector('.search-result-container');
let resultItems = []; let resultItems = [];
if (searchText.length > 0) { if (searchText.length > 0) {
// Perform local searching // Perform local searching
resultItems = getResultItems(keywords); resultItems = getResultItems(keywords);
} }
if (keywords.length === 1 && keywords[0] === '') { if (keywords.length === 1 && keywords[0] === '') {
resultContent.innerHTML = '<div class="no-result"><i class="fa fa-search fa-5x"></i></div>'; container.classList.add('no-result');
container.innerHTML = '<div class="search-result-icon"><i class="fa fa-search fa-5x"></i></div>';
} else if (resultItems.length === 0) { } else if (resultItems.length === 0) {
resultContent.innerHTML = '<div class="no-result"><i class="far fa-frown fa-5x"></i></div>'; container.classList.add('no-result');
container.innerHTML = '<div class="search-result-icon"><i class="far fa-frown fa-5x"></i></div>';
} else { } else {
resultItems.sort((left, right) => { resultItems.sort((left, right) => {
if (left.includedCount !== right.includedCount) { if (left.includedCount !== right.includedCount) {
@ -183,8 +185,13 @@ document.addEventListener('DOMContentLoaded', () => {
} }
return right.id - left.id; return right.id - left.id;
}); });
resultContent.innerHTML = `<ul class="search-result-list">${resultItems.map(result => result.item).join('')}</ul>`; const stats = CONFIG.i18n.hits.replace(/\$\{hits}/, resultItems.length);
window.pjax && window.pjax.refresh(resultContent);
container.classList.remove('no-result');
container.innerHTML = `<div class="search-stats">${stats}</div>
<hr>
<ul class="search-result-list">${resultItems.map(result => result.item).join('')}</ul>`;
window.pjax && window.pjax.refresh(container);
} }
}; };