Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 3351 additions and 487 deletions
<template>
<li :class="['definition', 'level'+level]" :ref="'def' + body.id" :id="'def' + body.id">
<li :class="['definition', 'level'+level]" :ref="level != 9 ? 'def' + body.id : ''" :id="level != 9? 'def' + body.id : ''">
<span v-if="level!=9"/>
<ul class="explanations">
<DefElement :body="explanation" :dictionary="dictionary" v-for="(explanation, index) in explanations" :key="index" @article-click="article_link_click" />
<DefElement :body="explanation" :dictionary="dictionary" :has_article_ref=has_article_ref(explanation) v-for="(explanation, index) in explanations" :key="index" @article-click="article_link_click" />
</ul>
<div v-if="examples.length">
<h4>{{example_header}}</h4>
<h5 :lang="lang_tag_locale" v-if="level < 3">{{$t('article.headings.examples', content_locale)}}</h5>
<ul class="examples">
<Example :body="example" :dictionary="dictionary" v-for="(example, index) in examples" :key="index" @article-click="article_link_click" />
</ul>
......@@ -13,7 +14,7 @@
<CompoundList :body="compound_list" :dictionary="dictionary" v-for="(compound_list, index) in compund_lists" :key="index" @article-click="article_link_click" />
</ul>
<div :is="level < 3 ? 'ol' : 'ul'" class="sub_definitions" v-if="subdefs.length">
<Definition :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :dictionary="dictionary" :key="index" @article-click="article_link_click" />
<Definition :def_number='index+1' :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :dictionary="dictionary" :key="index" @article-click="article_link_click" />
</div>
</li>
</template>
......@@ -28,7 +29,9 @@ var Definition = {
props: {
body: Object,
level: Number,
dictionary: String
dictionary: String,
def_number: Number
},
components: {
DefElement,
......@@ -38,41 +41,80 @@ var Definition = {
},
computed: {
explanations: function() {
try {
return this.body.elements.filter(el => el.type_ == 'explanation')
} catch (error) {
this.$emit('error', {location: "explanations", message: error.message})
return []
}
},
examples: function() {
try {
return this.body.elements.filter(el => el.type_ == 'example')
} catch (error) {
this.$emit('error', {location: "examples", message: error.message})
return []
}
},
compund_lists: function() {
try {
return this.body.elements.filter(el => el.type_ == 'compound_list')
},
example_header: function() {
return this.dictionary == 'bob' ? 'Eksempel' : 'Døme'
} catch (error) {
this.$emit('error', {location: "compound_lists", message: error.message})
return []
}
},
subdefs: function() {
try {
return this.body.elements.filter(el => el.type_ == 'definition').filter(def => def.elements.filter(el => el.type_ != 'sub_article').length > 0)
} catch (error) {
this.$emit('error', {location: "subdefs", message: error.message})
return []
}
// filtrerer bort definisjoner som bare inneholder underartikler
return this.body.elements.filter(el => el.type_ == 'definition').filter(def => def.elements.filter(el => el.type_ != 'sub_article').length > 0)
},
content_locale: function() {
return this.$parent.content_locale
},
lang_tag_locale: function() {
return this.$parent.lang_tag_locale
}
},
},
mounted: function() {
let ref = 'def' + this.body.id
if(location.hash.substring(1) == ref){
this.$refs[ref].scrollIntoView()
this.$refs[ref].scrollIntoView({block: 'center'})
this.$refs[ref].classList.add('highlighted')
}
},
methods: {
article_link_click: function(item) {
this.$emit('article-click', item)
}
},
has_article_ref: function(item){
if(item.items.length && item.items[0].type_ == "article_ref" && item.items[0].definition_id === undefined)
{
return "true";
}
else{
return "false";
}
}
},
watch:{
$route(to, from) {
let ref = 'def' + this.body.id
if(location.hash.substring(1) == ref){
this.$refs[ref].classList.add('highlighted')
}else{
this.$refs[ref].classList.remove('highlighted')
if (this.$refs[ref]) {
if(location.hash.substring(1) == ref){
this.$refs[ref].classList.add('highlighted')
}else {
this.$refs[ref].classList.remove('highlighted')
}
}
}
}
......@@ -85,15 +127,18 @@ q {
font-style: italic;
}
.highlighted {
.highlighted, mark {
background-color: var(--v-tertiary-darken1);
border-radius: 5px;
}
h4 {
color: var(--v-primary-base);
font-size: 14px;
padding-left: 12px;
padding-top: 6px;
mark {
font-weight: bold;
}
li[has_article_ref="true"] {
margin-top: 8px;
margin-left: -25px;
}
</style>
<template>
<main>
<div class="search_container">
<div class="lang_select_container">
<v-radio-group row v-model="lang">
<template v-slot:label>
<span>VIS</span>
</template>
<v-radio value="bob,nob" color="primary">
<template v-slot:label>
<span>
begge{{$vuetify.breakpoint.smAndDown ? '' : ' ordbøkene'}}
</span>
</template>
</v-radio>
<v-radio value="bob" color="primary">
<template v-slot:label>
<span>
{{$vuetify.breakpoint.xs ? 'bm' : 'bokmål (bm)'}}
</span>
</template>
</v-radio>
<v-radio value="nob" color="primary">
<template v-slot:label>
<span>
{{$vuetify.breakpoint.xs ? 'nn' : 'nynorsk (nn)'}}
</span>
</template>
</v-radio>
</v-radio-group>
<main tabindex="-1" ref="main" id="main" class="dict-container">
<span class="chosen_api" v-if="chosen_api">API: {{chosen_api}}</span>
<SearchForm ref="SearchForm"
v-on:submit="select_result"
v-on:update-lang-form="update_lang_form"
@updatePos="update_pos"
@updateScope="update_scope">
</SearchForm>
<div id="notifications"
v-if="$route.name && !error"
:class="$vuetify.breakpoint.name">
<div id="suggestions"
v-if="!article && !no_results">
<div class="search_notification"
v-if='inflection_suggestions && inflection_suggestions.length && this.queryString.slice(-1) != "."'>
<v-icon left
color="primary">info</v-icon><em>{{queryString}}</em> {{$t('notifications.inflected')}}<!--
--><span v-for="(item,index) in inflection_suggestions"
:key="index"><!--
--><router-link :to="generate_path({q: item[0]})"
@click.native="inflection_link(item[0])">{{item[0]}}</router-link><!--
-->{{index == inflection_suggestions.length-1? '.' : ', '}}</span>
</div>
<div class="search_notification"
v-if="lang=='bm,nn' && similar && similar.length > 0 && (search_results.nn && search_results.nn.length == 0)">
<v-icon left
color="primary">info</v-icon>{{$t('notifications.similar_nn')}}<!--
--><span v-for="(item,index) in similar"
:key="index"><!--
--><router-link :to="generate_path({q: queryString+'|'+item[0]})"
@click.native="other_dict(item[0])">{{item[0]}}</router-link><!--
-->{{index == similar.length-1? '.' : ', '}}
</span>
</div>
<div class="search_notification"
v-if="lang=='bm,nn' && similar && similar.length > 0 && (search_results.bm && search_results.bm.length == 0)">
<v-icon left
color="primary">info</v-icon>{{$t('notifications.similar_bm')}}<!--
--><span v-for="(item,index) in similar"
:key="index"><!--
--><router-link :to="generate_path({q: queryString+'|'+item[0]})"
@click.native="other_dict(item[0])">{{item[0]}}</router-link><!--
-->{{index == similar.length-1? '.' : ', '}}
</span>
</div>
</div>
<div id="return_to_results"
v-if="$vuetify.breakpoint.mdAndUp && article && $store.state.searchRoute">
<router-link id="return_link"
:to="$store.state.searchRoute"
@click.native="return_to_results()">
<v-icon left
class="nav_arrow">chevron_left</v-icon>{{$t("notifications.back")}}
</router-link>
</div>
<Autocomplete @submit="select_result" :endpoint="api_pref">
</Autocomplete>
<div class="no_results"
v-if="no_results && !error">
<div>
<p>
<v-icon left
color=primary>error</v-icon> <strong role="heading" aria-level="2" id="result0">{{no_results}}<span
v-if="pos_selected">{{$t('notifications.no_pos_results', {pos: $t('pos_tags_plural.'+pos_selected)})}}</span></strong>
</p>
<p class="below-notification" v-if="!article && inflection_suggestions && inflection_suggestions.length">
<em>{{this.queryString}}</em>{{$t('notifications.inflected')}}
<span v-for="(item,index) in inflection_suggestions"
:key="index"><!--
--><router-link :to="generate_path({q: item[0]})"
@click.native="inflection_link(item[0])">{{item[0]}}</router-link><!--
-->{{index == inflection_suggestions.length-1? '.' : ', '}}</span>
</p><p class="below-notification"
v-if="lang=='bm' && suggest_other_dict">{{$t('notifications.suggest_dict[1]')}}
<router-link :to="generate_lang_path('nn')"
@click.native="language_link('nn')">{{$t('dicts.nn')}}</router-link><!--
--></p>
<p class="below-notification"
v-if="lang=='nn' && suggest_other_dict">{{$t('notifications.suggest_dict[0]')}}<br>{{$t('notifications.suggest_dict[1]')}}
<router-link :to="generate_lang_path('bm')"
@click.native="language_link('bm')">{{$t('dicts_inline.bm')}}</router-link>
</p>
<div v-if="suggest_exact.length"
:class="'v-sheet v-card rounded-xl did_you_mean article ' + $vuetify.breakpoint.name">
<span class="similar-label" role="heading" aria-level="1">{{$t('notifications.similar')}}</span>
<v-list>
<template v-for="(item, index) in suggest_exact">
<v-list-item :key="index">
<router-link :to="generate_path({q: item[0], scope})"
@click.native="exact_link()">{{item[0]}}</router-link>
<span class="dict-parentheses"
v-if="lang=='bm,nn'">&nbsp;({{["bokmål","nynorsk","bokmål, nynorsk"][item[1]-1]}})</span>
</v-list-item>
</template>
</v-list>
</div>
<div v-if="suggest_fulltext.length"
:class="'v-sheet v-card rounded-xl did_you_mean article ' + $vuetify.breakpoint.name">
<span class="similar-label" role="heading" aria-level="1">{{$t('notifications.fulltext')}}</span>
<v-list>
<template v-for="(item, index) in suggest_fulltext">
<v-list-item :key="index">
<router-link :to="generate_path({q: item[0], scope: scope+'f'})"
@click.native="fulltext_link()">{{item[0]}}</router-link>
<span class="dict-parentheses"
v-if="lang=='bm,nn'">&nbsp;({{["bokmål","nynorsk","bokmål, nynorsk"][item[1]-1]}})</span>
</v-list-item>
</template>
</v-list>
</div>
<div v-if="similar && similar.length"
:class="'v-sheet v-card rounded-xl did_you_mean article ' + $vuetify.breakpoint.name">
<span class="similar-label" role="heading" aria-level="1">{{$t('notifications.similar')}}</span>
<v-list>
<template v-for="(item, index) in similar">
<v-list-item :key="index">
<router-link :to="generate_path({q: item[0]})"
@click.native="similar_link(item[0])">{{item[0]}}</router-link>
<span class="dict-parentheses"
v-if="lang=='bm,nn'">&nbsp;({{["bokmål","nynorsk","bokmål, nynorsk"][item[1]-1]}})</span>
</v-list-item>
</template>
</v-list>
</div>
</div>
</div>
</div>
<div id="spinner" v-if="waiting">
<v-progress-circular indeterminate color="secondary" size="120"></v-progress-circular>
<SearchResults :results_bm="search_results.bm || []"
:results_nn="search_results.nn || []"
:lang="lang"
:queryPattern="queryPattern"
:scope="scope"
@article-click="article_link_click"
@details-click="details_click"
@update-page="update_page"
v-if="$route.name && !article && !error && !no_results" />
<div id="spinner"
v-if="waiting">
<v-progress-circular indeterminate
color="secondary"
size="120"></v-progress-circular>
</div>
<SearchResults :hits="search_results"
:lang="lang"
@article-click="article_link_click"
@details-click="details_click"
v-if="search_results.length && ! waiting && ! article" />
<div id="single_article_container" v-if="article">
<div class="return_to_results" v-if="search_results">
<router-link :to="article.source" @click.native="return_to_results()">
<img class="nav_arrow" src="../assets/arrow_left.svg">Tilbake til {{article.dictionary == 'bob' ? 'søkeresultater' : 'søkjeresultat'}}
</router-link>
</div>
<Article :key="article_key" :article="article" @article-click="article_link_click" />
<div id="single_article_container"
v-if="article && !error && !no_results" :class="$store.state.searchRoute && $vuetify.breakpoint.mdAndUp && lang == 'bm,nn' ? article.dictionary : null">
<Article :key="article_key"
:article="article"
title_id="result0"
@article-click="article_link_click"
@details-click="details_click"
articleLookup/>
</div>
<div class="welcome" v-if="! (article || error || search_results.length || waiting)">
<div class="monthly" :class="$vuetify.breakpoint.name">
<div class="welcome-container" :class="$vuetify.breakpoint.name"
v-show="!error && ($route.name=='/' || !$route.name)">
<div class="welcome" :class="$vuetify.breakpoint.name">
<div class="monthly-title"><h2><span>{{$t('monthly')}}</span></h2></div>
<div class="monthly"
:class="$vuetify.breakpoint.name">
<div>
<Article :article="monthly_bm" @article-click="article_link_click" />
<Article :article="monthly_bm"
title_id="result0"
@article-click="article_link_click"
@details-click="details_click" />
</div>
<div>
<Article :article="monthly_nn" @article-click="article_link_click" />
<Article :article="monthly_nn"
title_id="result1"
@article-click="article_link_click"
@details-click="details_click" />
</div>
</div>
</div>
<div class="error" v-if="error">
<h1>Oooops...</h1>
{{error}}
</div>
<div class="error"
v-if="error">
<div>
<h1 tabindex="0"
id="result0">{{error.title}}</h1>
<p>{{error.description}}</p>
</div>
</div>
<div v-if="$route.name && $route.name != 'lookup' && $store.state.currentLocale != 'ukr' && !$parent.waiting" class="betalink notification mx-auto mb-10"
centered
>
<div class="d-flex flex-column align-md-center flex-md-row mx-2">
<div class="shrink justify-top mr-8" cols="12" sm="6">
<img aria-hidden="true" width="96" height="96" src="https://test.ordbokene.no/favicon.ico">
</div>
<div>
<div v-if="$store.state.currentLocale == 'nob'">
<h2>Betaversjon</h2>
Vi har lansert en betaversjon av denne nettsiden med bedre søkeforslag og forslag til oversettelser mellom bokmål og nynorsk.<div class="mt-2"> <a :href="beta_search">Søk i betaversjonen</a><v-icon small color="white">launch</v-icon></div>
</div>
<div v-if="$store.state.currentLocale == 'nno'">
<h2>Betaversjon</h2>
Vi har lansert ein betaversjon av denne nettstaden med betre søkjeforslag og forslag til omsetjingar mellom bokmål og nynorsk. <div class="mt-2"><a :href="beta_search">Søk i betaversjonen</a><v-icon small color="white">launch</v-icon></div>
</div>
<div v-if="$store.state.currentLocale == 'eng'">
<h2>Beta version</h2>
We have launched a beta version of this website with better search suggestions and suggested translations between Bokmål and Nynorsk.<div class="mt-2"><a :href="beta_search">Search in the beta version</a><v-icon small color="white">launch</v-icon></div>
</div>
</div>
</div>
</div>
</main>
</template>
......@@ -70,254 +224,880 @@ import axios from "axios"
import entities from '../utils/entities.js'
import Article from './Article.vue'
import SearchResults from './SearchResults.vue'
import Autocomplete from './Autocomplete.vue'
import SearchForm from './SearchForm.vue'
var api_endpoint = 'https://beta.ordbok.uib.no/api/dict'
const ENDPOINTS = {
'oda_prod': process.env.VUE_APP_ODA_PROD,
'oda_dev': process.env.VUE_APP_ODA_DEV,
'odd_prod': process.env.VUE_APP_ODD_PROD,
'odd_dev': process.env.VUE_APP_ODD_DEV,
'ida_dev': process.env.VUE_APP_IDA_DEV,
'ida_prod': process.env.VUE_APP_IDA_PROD,
}
const ENDPOINT = ENDPOINTS[process.env.VUE_APP_ENDPOINT]
const FALLBACK_ENDPOINT = ENDPOINTS[process.env.VUE_APP_FALLBACK_ENDPOINT]
function compare_by_hgno(lemma_text) {
return function(art1, art2) {
let lemma1 = art1.lemmas.filter(lemma => lemma.lemma == lemma_text)[0]
let lemma2 = art2.lemmas.filter(lemma => lemma.lemma == lemma_text)[0]
return lemma1.hgno - lemma2.hgno
}
}
function navigate_to_article(self, origin) {
self.article = null
self.waiting_for_articles = true
axios.interceptors.request.use(function (config) {
config.headers["x-api-key"] = "ZkYiyRVXxH86ijsvhx3cH4SY5Iik2ijI3BKVJGMm"
return config;
}, function (error) {
return Promise.reject(error);
});
const lang = self.$route.params.lang
function navigate_to_article(self, source) {
axios.get(api_endpoint + '/' + self.$route.params.lang + '/article/' + self.$route.params.id)
self.api.get(lang + '/article/' + self.$route.params.id + ".json")
//self.api.get("https://httpstat.us/502")
.then(function(response){
self.article = Object.assign(response.data, {'dictionary': self.$route.params.lang, source: source, results: self.search_results})
self.search_results = []
self.article = Object.assign(response.data, {'dictionary': lang, results: self.search_results})
self.error = null
})
.catch(function(error){
if (error.response && error.response.status == 404) {
self.error = "Vi har ingen artikkel med id " + self.$route.params.id
} else {
self.error = "Noe gikk galt..."
}
self.handle_error(error, {retry: navigate_to_article, arg: origin, article: true})
})
.then(function(response){
self.waiting_for_articles = false
history.replaceState({article: self.article, search_results: [], lang: self.lang, error: self.error}, '')
if (source) {
self.$plausible.trackEvent('internal link incoming', {props: {origin: source}})
}
self.replace_history()
if (origin) self.$plausible.trackEvent(' incoming', {props: {origin}})
})
}
function navigate_to_search(self, query) {
axios.get(self.api_pref + 'search?q=' + query)
.then(function(response){
self.search_results = response.data
if (! self.search_results.length) {
self.error = "Vi fant ingen resultater for '" + decodeURIComponent(query) + "'. (Søkeforlag kommer i en senere oppatering av Ordbøkene)"
} else {
self.error = null
}
})
.catch(function(error){
if (error.response && error.response.status == 400) {
self.error = "Søkeuttrykket inneholder feil"
} else if (error.response) {
self.error = "Noe gikk galt på serversiden"
} else {
self.error = "Nettverksproblemer, prøv igjen"
}
})
.then(function(_){
self.waiting_for_articles = false
history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang, error: self.error}, '')
})
async function load_articles(self, query, offset, n, dict) {
let article_IDs = self.article_info.articles[dict]
if (article_IDs)
{
if (offset > article_IDs.length) {
n = 0
}
else if (offset + n > article_IDs.length) {
n = article_IDs.length % n
}
if (n > 0 && (self.lang == dict || self.lang == "bm,nn")) {
article_IDs = article_IDs.slice(offset, offset + n)
return Promise.all(article_IDs.map((article_id) => {
return self.api.get(`${dict}/article/${article_id}.json`)
//return self.api.get(`https://httpstat.us/502`)
}))
.then((response) => {
let results = response.map((element, index) => {
return Object.assign(element.data, {
dictionary: dict
})
})
self.article = null
self.search_results[dict] = results
})
.catch(error => {
self.handle_error(error, {})
})
}
else {
self.search_results[dict] = []
}
}
return Promise.resolve()
}
function navigate_to_word(self, word) {
axios.get(self.api_pref + 'suggest?q=' + word)
.then(function(response){
self.search_results = response.data.filter(result => result.match.length == word.length).sort(compare_by_hgno(word))
if (! self.search_results.length) {
self.error = "Ordet '" + decodeURIComponent(word) + "' finnes ikke i ordbøkene"
} else {
self.error = null
function navigate_to_query(self, word, keep_page) {
self.error = null
self.no_results = null
self.waiting_for_articles = true
self.inflection_suggestions = []
self.similar = []
self.suggest_fulltext = []
self.suggest_exact = []
self.suggest_other_dict = false
if (!self.event) {
self.event = {match: word}
}
let query = self.event
let q = query.match
let words = q.split(/ |\|/)
if (words.length > 20) {
self.no_results = self.$t('notifications.ignored_words')
self.waiting_for_articles = false
self.replace_history()
return
}
for (let i = 0; i < words.length; i++) {
if (words[i].length > 40) {
self.no_results = self.$t('notifications.ignored_chars')
self.waiting_for_articles = false
self.replace_history()
return
}
})
.catch(function(error){
if (error.response) {
self.error = "Noe gikk galt på serversiden"
} else {
self.error = "Nettverksproblemer, prøv igjen"
}
if (!keep_page) {
self.page = 1
}
let advanced_search = /[?_*%|]/.test(q)
// Get inflections
if (!advanced_search && self.$route.name == 'search') {
let params = {q, dict: self.lang, dform: 'int', include: "i", meta: 'n', wc: self.pos_selected}
self.api.get('api/suggest?', {params})
//self.api.get('https://httpstat.us/502')
.then((response) => {
self.inflection_suggestions = response.data.a.inflect && response.data.a.inflect.filter((item) => item[0][0] != "-" && item[0][item.length-1] != "-")
console.log(self.inflection_suggestions)
}).catch(error =>{
self.handle_error(error, {retry: navigate_to_query, arg: q})
self.replace_history()
})
}
})
.then(function(_){
self.waiting_for_articles = false
history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang, error: self.error}, '')
})
// Get article IDs
let params = {w: query.match, dict: self.lang, scope: self.scope}
let offset = 0
if (self.page) {
offset = self.perPage * (self.page -1)
}
if (self.pos_selected) params.wc = self.pos_selected
self.api.get('api/articles?', {params}).then((response) => {
//self.api.get('https://httpstat.us/502', {params}).then((response) => {
self.article_info = response.data
self.search_results = {}
let bm_length = response.data.articles.bm ? response.data.articles.bm.length : 0
let nn_length = response.data.articles.nn ? response.data.articles.nn.length : 0
let total_length = bm_length + nn_length
let dict = self.lang
// Similar
if (bm_length == 0 || nn_length == 0) {
if (!advanced_search) {
if (dict == 'bm,nn' && total_length > 0) {
dict = bm_length == 0? 'bm' : 'nn'
}
let params = {q, dict, dform: 'int', include: "s", wc: self.pos_selected}
self.api.get('api/suggest?', {params})
//axios.get('https://httpstat.us/502')
.then((response) => {
if (self.suggest_exact && self.suggest_exact.length == 0) {
self.similar = response.data.a.similar
}
self.replace_history()
}).catch(error => {
self.handle_error(error, {retry: navigate_to_query, arg: q})
self.replace_history()
})
} else {
self.similar = []
}
}
if (total_length == 0) {
self.waiting_for_articles = false
self.no_results = self.$t('notifications.no_results')
let params = {q, dict, n: 2, dform: 'int', include: 'ef', wc: self.pos_selected}
self.api.get('api/suggest?', {params}).then((response) => {
if (response.data.a.exact && response.data.a.exact[0][0].toUpperCase() == q) {
console.log(response.data.a.exact)
self.suggest_exact = response.data.a.exact || []
self.suggest_fulltext = []
self.similar = []
}
else {
self.suggest_fulltext = response.data.a.freetext || []
self.suggest_exact = []
}
}).catch(error => {
self.handle_error(error, {retry: navigate_to_query, arg: q})
self.replace_history()
})
if (dict != 'bm,nn') {
let params = {q, n: 2, dict: dict=='bm'?'nn':'bm', dform: 'int', include: 'e', wc: self.pos_selected}
self.api.get('api/suggest?', {params}).then((response) => {
self.suggest_other_dict = response.data.cnt > 0 && response.data.a.exact[0][0] == q
}).catch(error => {
self.handle_error(error, {retry: navigate_to_query, arg: q})
self.replace_history()
})
}
self.replace_history() // fixes routing bug when going back from suggested search
}
else {
self.no_results = false
Promise.all([
load_articles(self, query, offset, self.perPage, "bm"),
load_articles(self, query, offset, self.perPage, "nn")
])
.then(() => {
self.waiting_for_articles = false
self.$store.commit('setSearchRoute', self.$route.fullPath)
self.replace_history()
})
}
}).catch(error =>{
self.handle_error(error, {retry: navigate_to_query, arg: q})
self.replace_history()
})
}
export default {
name: 'DictionaryView',
data: function() {
return {
api: null,
chosen_api: null,
fallback: false,
article_key: 0,
search_results: [],
lang: 'bob,nob',
search_results: {},
lang: this.$store.state.defaultDict,
waiting_for_articles: true,
waiting_for_metadata: true,
article: null,
error: null,
no_results: false,
monthly_nn: null,
monthly_bm: null,
monthly_nn: null
event: null,
scope: "ei",
pos_selected: "ALL",
article_info: null,
page: 1,
perPage: 10,
inflection_suggestions: null,
similar: null,
selected: null,
suggest_fulltext: [],
suggest_exact: [],
suggest_other_dict: false
}
},
computed: {
beta_search: function(){
const q = this.queryPattern
const advanced_search = this.scope.includes('f') || this.pos_selected || /[?_*%|]/.test(q)
const base = "https://beta.ordbokene.no/" + this.$i18n.locale
if (advanced_search) {
return `${base}/search?q=${q}&dict=${this.lang}&scope=${this.scope}${this.pos ? '&pos=' + this.pos : ''}`
}
else {
return `${base}/${this.lang}?q=${q}`
}
},
queryString: function() {
let q = this.$route.query.q || this.$route.params.q
return q ? q.trim() : ""
},
queryPattern: function() {
if (this.queryString) {
if (/[_%|]/.test(this.queryString)) {
return this.queryString.replaceAll(/[*%]/g, ".*").replaceAll(/[_?]/g, ".")
}
else {
return this.queryString
}
}
},
waiting: function() {
return (this.waiting_for_articles || this.waiting_for_metadata) && this.$route.name != 'root'
},
api_pref: function() {
return api_endpoint + '/' + this.lang + '/article/'
}
},
metaInfo() {
let advanced = /[?_*%|]/.test(this.queryString)
if (this.no_results || advanced || this.scope.includes("f")) {
return {meta: [{name: "robots", content: 'noindex'}]}
}
else if (!this.articleLookup) {
let title
let meta
let link = []
let q = ""
if (this.queryString) {
q = this.queryString + (this.lang == 'bm,nn' ? ' - ' : ' | ')
link = [{rel: "canonical", href: `https://ordbokene.no/${this.lang}/${this.queryString}`} ]
}
let desc = " viser skrivemåte og bøying i tråd med norsk rettskriving. Språkrådet og Universitetet i Bergen står bak ordbøkene."
if (this.lang == 'bm,nn') {
title = q+'ordbøkene.no'
meta = [{name: "description", vmid: 'description', content: "Bokmålsordboka og Nynorskordboka"+desc}]
}
if (this.lang == 'bm') {
title = q+"Bokmålsordboka"
meta = [{name: "description", vmid: 'description', content: "Bokmålsordboka"+desc}]
}
if (this.lang == 'nn') {
title = q+"Nynorskordboka"
meta = [{name: "description", vmid: 'description', content: "Nynorskordboka"+desc}]
}
return {title,
meta,
link}
}
},
components: {
Article,
Autocomplete,
SearchForm,
SearchResults
},
methods: {
select_result: function(event) {
if(event.articles){
let source = '/' + this.lang + '/w/' + event.word
this.$router.push(source)
this.search_results = event.articles.sort(compare_by_hgno(decodeURIComponent(event.word))).map(a => Object.assign(a, {source: source}))
this.article = null
this.error = null
history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang, error: this.error}, '')
this.$plausible.trackEvent('dropdown selection', {props: {query: event.label, match: event.match}})
}else{
this.waiting_for_articles = true
this.article = null
this.$router.push(`/${this.lang}/search/${event.q}`)
navigate_to_search(this, event.q)
this.$plausible.trackEvent('dropdown selection', {props: {query: event.label, match: '<fritekstsøk>'}})
}
},
article_link_click: function(item) {
if (this.article && this.article.article_id == item.article_id){
this.article_key++
history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang, error: this.error}, '')
}else{
this.article = null
this.waiting_for_articles = true
navigate_to_article(this, item.source)
}
},
details_click: function(item) {
this.article = item.article
history.replaceState({article: this.article, search_results: [], lang: this.lang, error: null}, '')
},
return_to_results: function() {
this.article = null
}
},
mounted: function(){
let self = this
this.lang = 'bob,nob'
Promise.all([
axios.get(api_endpoint + '/bob').then(function(response){
load_welcome_and_metadata: function() {
let self = this
Promise.all([
self.api.get('bm/concepts.json').then(function(response){
let concepts = response.data.concepts
entities.bob = concepts
entities.bm = concepts
}),
axios.get(api_endpoint + '/nob').then(function(response){
self.api.get('nn/concepts.json').then(function(response){
let concepts = response.data.concepts
entities.nob = concepts
entities.nn = concepts
})
]).then(function(_) {
self.waiting_for_metadata = false
if(self.$route.name == 'word') {
self.lang = self.$route.params.lang
navigate_to_word(self, self.$route.params.word)
if (self.$route.name == 'search') {
if (self.queryString) {
navigate_to_query(self, self.queryString, true)
}
else {
self.$router.push( "/"+self.lang)
self.waiting_for_articles = false
self.replace_history()
self.load_monthly("bm")
self.load_monthly("nn")
}
}
else if(self.$route.name == 'lookup'){
navigate_to_article(self, self.$route.params.id)
else if(self.$route.name == 'word') {
self.scope = 'ei'
self.pos = null
navigate_to_query(self, self.queryString, true)
}
else if (self.$route.name == 'search') {
self.lang = self.$route.params.lang
navigate_to_search(self, self.$route.params.query)
else if(self.$route.name == 'lookup'){
navigate_to_article(self, self.$route.path)
}
else {
self.lang = self.$route.params.lang || 'bob,nob'
self.waiting_for_articles = false
history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang, error: self.error}, '')
self.replace_history()
self.load_monthly("bm")
self.load_monthly("nn")
}
}).catch(function(error){
self.api = axios.create({baseURL: FALLBACK_ENDPOINT})
if (!self.error || !self.error.response) {
if (self.fallback) {
if (error.response) {
self.error = {title: self.$t('error.server.title'), description: self.$t('error.server.description', {code: error.response.status}), response: error.response}
}
else if (error.message == "Network Error") {
self.error = {title: self.$t('error.network.title'), description: self.$t('error.network.description')}
}
else {
self.error = {title: self.$t('error.generic.title'), description: self.$t('error.generic.description')}
}
self.waiting_for_metadata = false
self.waiting_for_articles = false
}
else {
self.fallback = true
self.load_welcome_and_metadata()
}
}
})
},
replace_history: function() {
history.replaceState({article: this.article,
search_results: this.search_results,
article_info: this.article_info,
lang: this.lang,
error: this.error,
no_results: this.no_results,
pos_selected: this.pos_selected,
scope: this.scope,
page: this.page,
perPage: this.perPage,
inflection_suggestions: this.inflection_suggestions,
similar: this.similar,
suggest_fulltext: this.suggest_fulltext,
suggest_exact: this.suggest_exact,
suggest_other_dict: this.suggest_other_dict}, '')
},
total_results: function() {
if (this.article_info) {
let total = 0
if (this.article_info.articles.bm) {
total += this.article_info.articles.bm.length
}
if (this.article_info.articles.nn) {
total += this.article_info.articles.nn.length
}
return total
}
},
// words of the month
axios.get(api_endpoint + '/bob/article/5607').then(function(response){
self.monthly_bm = Object.assign(response.data, {dictionary: 'bob'})
load_monthly: function(dict) {
let self = this
this.api.get(dict + '/parameters.json').then(function(response) {
let front_article_id = response.data.front_article.value
self.api.get(dict + `/article/${front_article_id}.json`).then(function(response){
if (dict == "nn") {
self.monthly_nn = Object.assign(response.data, {dictionary: 'nn'})
}
else {
self.monthly_bm = Object.assign(response.data, {dictionary: 'bm'})
}
})
})
axios.get(api_endpoint + '/nob/article/78569').then(function(response){
self.monthly_nn = Object.assign(response.data, {dictionary: 'nob'})
},
handle_error: function(error, retry_params) {
console.log(error)
this.waiting_for_articles = false
this.no_results = false
this.search_results = {}
this.inflection_suggestions = []
this.similar = []
this.suggest_fulltext = []
this.suggest_exact = []
this.suggest_other_dict = false
if (!this.chosen_api) {
this.api = axios.create({baseURL: FALLBACK_ENDPOINT})
}
if (!this.error || !this.error.response) {
if (this.fallback || !retry_params.retry) {
this.fallback = true
if (error.response) {
if (error.response.status == 404) {
if (retry_params.article) {
this.error = {title: this.$t('error.404.title'), description: this.$t('error.no_article', {id: this.$route.params.id}), article: true, response: error.response}
}
else {
this.error = {title: this.$t('error.404.title'), description: this.$t('error.404.description'), article: retry_params.article, response: error.response}
}
}
else if (error.response.status == 503) {
this.error = {title: this.$t('error.503.title'), description: this.$t('error.503.description'), article: retry_params.article, response: error.response}
}
else if (String(error.response.status)[0] == "5") {
this.error = {title: this.$t('error.server.title'), description: this.$t('error.server.description', {code: error.response.status}), article: retry_params.article, response: error.response}
}
else {
this.error = {title: this.$t('error.generic_code.title'), description: this.$t('error.generic_code.description', {code: error.response.status}), article: retry_params.article, response: error.response}
}
} else if (error.message == "Network Error") {
this.error = {title: this.$t('error.network.title'), description: this.$t('error.network.description'), article: retry_params.article, response: error.response}
}
else {
this.error = {title: this.$t('error.generic.title'), description: this.$t('error.generic.description'), article: retry_params.article, response: error.response}
}
} else {
this.fallback = true
retry_params.retry(this, retry_params.arg)
}
}
},
inflection_link: function (word) {
this.$plausible.trackEvent('inflection link', {props: {lang: this.previous.params.lang, from: this.previous.query.q, to: word}})
this.event = null
navigate_to_query(this, word)
},
other_dict: function(word) {
let lang = this.previous.params.lang
let from = this.previous.query.q
let to = word
this.$plausible.trackEvent('other dict', {props: {lang, from, to , words: lang+": "+from + " => " + to}})
this.event = null
navigate_to_query(this, this.queryString)
},
similar_link: function (word) {
let lang = this.previous.params.lang
let from = this.previous.query.q
let to = word
this.$plausible.trackEvent('similar link', {props: {lang, from, to , words: lang+": "+from + " => " + to}})
this.event = null
navigate_to_query(this, this.queryString)
},
fulltext_link: function () {
this.event = null
this.scope = this.scope + "f"
navigate_to_query(this, this.queryString)
},
exact_link: function () {
this.event = null
this.scope = this.scope + "ei"
navigate_to_query(this, this.queryString)
},
language_link: function (lang) {
this.lang = lang
this.event = null
navigate_to_query(this, this.queryString)
},
select_result: function (event) {
this.event = event
let path = `/${this.lang}/search`
let pos = this.pos_param()
let query = {q: event.match || event.q}
if (pos) query["pos"] = pos
if (this.scope) query["scope"] = this.scope
this.$router.push({path, query})
navigate_to_query(this)
// Tracking
let track_props = {query: event.q}
if (event.match) track_props.match = event.match
this.$plausible.trackEvent('dropdown selection', { props: track_props })
},
pos_param: function() {
if (this.pos_selected) return this.pos_selected.toLowerCase()
return null
},
update_page: function() {
this.waiting_for_articles = true
let q = this.queryString
let path = `/${this.lang}/search`
let pos = this.pos_param()
let query = {q: q, page: this.page}
if (pos != 'all') query.pos = pos
if (this.scope) query.scope = this.scope
if (this.perPage) query.perPage = this.perPage
this.$router.push({path, query})
let offset = 0
if (this.page) {
offset = this.perPage * (this.page -1)
}
let self = this
Promise.all([
load_articles(this, query, offset, this.perPage, "bm"),
load_articles(this, query, offset, this.perPage, "nn")]).then(() => {
self.replace_history()
self.$forceUpdate()
/*
// Debugging
if (self.page < Math.ceil(Math.max(self.article_info.articles.bm.length, self.article_info.articles.nn.length)/self.perPage)) {
self.page+=1
self.update_page()
}
*/
}
).then(() => {
this.$store.commit('setSearchRoute', this.$route.fullPath)
this.waiting_for_articles = false
})
}).catch(function(_){
self.error = "Et nettverksproblem hindret lasting av siden. Prøv å laste siden på nytt"
self.waiting_for_metadata = false
self.waiting_for_articles = false
})
},
generate_path: function(params) {
if (this.$route.name == "word") {
return this.$router.resolve({name: "search", query: {q: this.queryString, ...params}}).href
}
else {
return this.$router.resolve({query: {...this.queryString, ...params}}).href
}
},
generate_lang_path: function(dict) {
return this.$route.fullPath.replace(/\/(bm|nn|bm,nn)\//, "/"+dict+"/")
},
reload_params: function() {
let q = this.queryString
if (q) {
let path = `/${this.lang}/search`
let pos = this.pos_param()
let query = {q}
if (pos) query.pos = pos
if (this.scope) query.scope = this.scope
if (this.scope) query.scope = this.scope
if (this.perPage) query.perPage = this.perPage
this.$router.push({path, query})
navigate_to_query(this, q)
}
else {
this.$router.push({path: '/'+this.lang})
this.replace_history()
}
},
update_lang_form: function (lang) {
this.lang = lang
this.$store.commit("setDefaultDict", lang)
this.page = 1
},
update_scope: function(scope) {
this.scope = scope
if (this.$route.name && this.$route.name != 'lookup') {
this.page = 1
this.reload_params()
}
},
update_pos: function (pos) {
this.pos_selected = pos
if (this.$route.name && this.$route.name != 'lookup') {
this.page = 1
this.reload_params()
}
},
update_per_page: function(perPage) {
this.perPage = perPage
this.$store.commit('setPerPage', this.perPage)
this.page = 1
this.reload_params()
},
article_link_click: function(item) {
let event = window.event
if (!(event.ctrlKey || event.shiftKey || event.metaKey)) {
if (this.article && this.article.article_id == item.article_id){
this.article_key++
this.replace_history()
}else{
navigate_to_article(this, item.source)
}
}
},
details_click: function(item) {
let event = window.event
if (!(event.ctrlKey || event.shiftKey || event.metaKey )) {
this.article = item.article
this.replace_history()
}
},
return_to_results: function() {
this.article = null
this.replace_history()
},
set_fulltext_highlight: function() {
if (this.queryString && this.scope.includes("f")) {
let q = this.queryString
q = q.replace(/\*|%/, "[^\\s]*")
q = q.replace(/_|\?/, "[^\\s]")
this.$store.commit('setFulltextHighlight', q)
}
else {
this.$store.commit('setFulltextHighlight', false)
}
}
},
mounted: function(){
this.chosen_api = this.$route.query.api
if (this.chosen_api) {
this.api = axios.create({baseURL: ENDPOINTS[this.chosen_api]})
}
else {
this.api = axios.create({baseURL: ENDPOINT})
}
this.lang = this.$route.params.lang || this.$store.state.defaultDict || 'bm,nn'
if (this.$route.query.pos) {
this.pos_selected = this.$route.query.pos.toUpperCase()
} else this.pos_selected = null
if (this.$route.query.scope) {
this.scope = this.$route.query.scope
this.set_fulltext_highlight()
}
if (this.$route.query.page) this.page = parseInt(this.$route.query.page)
if (this.$route.query.perPage) {
this.perPage = parseInt(this.$route.query.perPage)
}
else {
this.perPage = parseInt(this.$store.state.perPage)
}
this.load_welcome_and_metadata()
},
watch: {
$route() {
this.$plausible.trackEvent('language', {props: {code: this.$route.params.lang}})
$route(to, from) {
this.previous = from
if (to.fullPath == "/") {
this.load_monthly("bm")
this.load_monthly("nn")
}
if (to.name == 'lookup' && from.fullPath == '/') {
this.$store.commit('setSearchRoute', null)
}
if (to.name == 'search') {
this.set_fulltext_highlight()
}
}
},
created: function() {
let self = this
window.onpopstate = function (event) {
if (event.state) {
self.article = event.state.article
self.search_results = event.state.search_results
self.lang = event.state.lang
self.error = event.state.error
if (event.state.lang) {
if (self.$refs.SearchForm.$refs.autocomplete.$refs.input == document.activeElement) {
self.$refs.SearchForm.$refs.autocomplete.$refs.input.select()
}
self.article = event.state.article
self.search_results = event.state.search_results
self.article_info = event.state.article_info
self.lang = event.state.lang
self.pos_selected = event.state.pos_selected
self.scope = event.state.scope
self.error = event.state.error
self.no_results = event.state.no_results
self.page = event.state.page,
self.perPage = event.state.perPage
self.inflection_suggestions = event.state.inflection_suggestions,
self.similar = event.state.similar,
self.suggest_fulltext = event.state.suggest_fulltext,
self.suggest_exact = event.state.suggest_exact,
self.suggest_other_dict = event.state.suggest_other_dict
if (!self.$route.hash && self.$route.name != 'search') {
history.scrollRestoration = 'manual'
window.scrollTo(0,0)
}
else {
history.scrollRestoration = 'auto'
}
}
else {
console.log("Navigation error")
}
}
}
}
}
</script>
<style>
main {
flex: 1 0 auto;
background-color: var(--v-tertiary-base);
.dict-container {
display: flex;
flex-direction: column;
flex-flow: column;
height: 100%;
}
div.welcome {
flex-grow: 10;
background-image: url('../assets/books.jpg');
div.welcome-container.lg, div.welcome-container.md, div.welcome-container.xl {
background-image: url("../assets/background.jpg");
background-repeat: no-repeat;
background-position-x: center;
background-size: cover !important;
flex: 1 1 auto;
}
div.welcome.lg, div.welcome.md, div.welcome.xl {
padding-top: 10px;
margin-bottom: auto;
flex: 1 1 auto;
flex-direction: column;
}
div.welcome-container.sm {
background-position-y: -128px;
}
div.welcome-container.md {
background-position-y: -160px;
}
div.welcome article {
div.welcome-comtainer.lg {
background-position-y: -256px;
}
div.welcome-container.xl {
background-position-y: -512px;
}
div.welcome .article {
border-style: none;
}
.search_container {
background-color: var(--v-tertiary-base);
padding-top: 1px;
padding-bottom: 10px;
#spinner {
margin: auto;
}
.dict-container>div, .dict-container>section, .welcome {
padding-left: calc((100vw - 1200px) / 2);
padding-right: calc((100vw - 1200px) / 2);
}
#search_results, #spinner, #single_article_container, div.welcome, div.search_container, .error {
padding-left: calc((100vw - 1000px) / 2);
padding-right: calc((100vw - 1000px) / 2);
.dict-container>.welcome-container {
padding: 0 !important;
}
div.welcome {
padding-left: calc((100vw - 917px) / 2);
padding-right: calc((100vw - 917px) / 2);
}
#single_article_container {
box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
background-color: white;
height: 100%;
}
#single_article_container.nn {
justify-content: flex-end !important;
display: flex;
}
#single_article_container.bm .article{
width: 50%;
}
#notifications .search_notification {
padding-top: 10px;
padding-bottom: 0px;
margin-left: 10px;
font-size: 18px;
}
#suggestions {
padding-left: 10px;
}
.error p, .no_results p {
margin-left: 15px;
}
.no_results {
padding-top: 24px;
}
.error div{
padding: 10px;
padding-top: 24px;
}
#spinner {
......@@ -337,24 +1117,21 @@ div.monthly.sm, div.monthly.xs {
flex-direction: column;
}
div.monthly article.bob .dict-label::before {
content: "fra ";
}
div.monthly article.nob .dict-label::before {
content: "frå ";
}
div.monthly details, div.monthly h3 {
display: none;
}
.v-label span {
color: var(--v-primary-base);
}
.lang_select_container {
padding-left: 15px;
padding-left: 10px;
}
.pos_select_container {
padding-left: 10px;
padding-right: 10px;
padding-bottom: 0px;
padding-top: 10px;
}
li.suggestion {
......@@ -371,12 +1148,130 @@ li.suggestion {
color: white;
}
.return_to_results {
#return_to_results {
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
display: table-cell;
}
#return_to_results a {
color: var(--v-text-base) !important;
text-decoration: none;
}
.nav_arrow {
vertical-align: middle;
color: var(--v-primary-base) !important;
}
.col {
padding: 10px;
}
.below-notification {
padding-left: 10px;
}
.did_you_mean.md, .did_you_mean.lg, .did_you_mean.xl {
max-width: 40%;
}
.dict-parentheses {
color: rgba(0,0,0,0.6);
font-size: 85%;
}
.monthly-title {
text-align: center;
font-size: 1.17em;
color: var(--v-primary-base);
margin-bottom: 10px;
margin-top: 10px;
}
.monthly-title h2 {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
text-align: center;
}
.xl .monthly-title h2, .lg .monthly-title h2, .md .monthly-title h2 {
padding-right: 10px;
padding-left: 10px;
}
.xl .monthly-title, .lg .monthly-title, .md .monthly-title {
background: linear-gradient(90deg, rgba(255,255,255,0) 10%, rgba(255, 255, 255, 0.75) 50%, rgba(255,255,255,0) 90%);
margin: 24px;
}
.monthly-title h2:after {
margin: 0 0 0 20px;
}
.xl .monthly-title, .lg .monthly-title, .md .monthly-title {
margin-top: 24px;
margin-bottom: 24px;
}
.xs .monthly-title h2:before,
.sm .monthly-title h2:before,
.xs .monthly-title h2:after,
.sm .monthly-title h2:after {
content: '';
margin: 0 24px 0 24px;
border-top: 2px solid;
flex: 1 0 0px;
color: var(--v-secondary-base);
}
.similar-label {
color: var(--v-primary-base) ;
font-weight: bold;
position: absolute;
padding-left: 10px;
top: 0;
font-variant-caps: all-small-caps;
font-size: 1.17em;
}
.chosen_api {
position: absolute;
top: 0px;
left: 0px;
color: white;
width: 100px;
}
.v-text-field--rounded > .v-input__control > .v-input__slot {
padding-left: 12px !important;
}
.betalink>div {
border-radius: 16px;
margin-top: 10px;
padding-left: 32px;
padding-right: 32px;
padding-top: 10px;
padding-bottom: 24px;
color: white;
background: black;
}
.betalink h2 {
font-variant: all-small-caps;
font-size: 2rem;
margin-bottom: 6px;
}
.betalink a {
color: white !important;
font-size: 1.25rem;
}
</style>
<template>
<li class="example">
<DefElement tag="q" :body="body.quote" @article-click="article_link_click" :dictionary="dictionary" /><span v-if="body.explanation && body.explanation.content.length"> - </span>
<DefElement tag="q" :body="body.quote" @article-click="article_link_click" :dictionary="dictionary" /><span v-if="body.explanation && body.explanation.content.length"> </span>
<DefElement tag="span" :body="body.explanation" v-if="body.explanation && body.explanation.content.length" @article-click="article_link_click" :dictionary="dictionary" />
</li>
</template>
......
<template>
<span>
<v-btn v-if="$route.name!='about'" to="/om" small dark text>
<v-icon small left>info</v-icon>{{$t('menu.about')}}
</v-btn>
<v-btn v-if="$route.name=='about'" to="/" small dark text>
<v-icon small left>home</v-icon>{{$t('home')}}
</v-btn>
<v-dialog :fullscreen="$vuetify.breakpoint.smAndDown" max-width="800px" v-model="help_dialog">
<template v-slot:activator="{ on, attrs }">
<v-btn small dark text v-on="on" v-bind="attrs">
<v-icon small left>help</v-icon>{{$t('menu.help')}}
</v-btn>
</template>
<v-card>
<v-toolbar elevation="0" dark color="primary">
<v-toolbar-title>{{$t('menu.help')}}</v-toolbar-title><v-spacer></v-spacer>
<v-toolbar-items><v-btn @click.native="help_dialog=false" text>{{$t('close')}}<v-icon right>close</v-icon></v-btn></v-toolbar-items>
</v-toolbar>
<v-card-text class="text--primary">
<br>
<Help/>
</v-card-text>
</v-card>
</v-dialog>
<v-dialog :fullscreen="$vuetify.breakpoint.smAndDown" max-width="800px" v-model="settings_dialog">
<template v-slot:activator="{ on, attrs }">
<v-btn small dark text v-on="on" v-bind="attrs">
<v-icon small left>settings</v-icon><span>{{$t('settings.title')}}</span>
</v-btn>
</template>
<v-card>
<v-toolbar elevation="0" dark color="primary">
<v-toolbar-title><span role="heading" aria-level="1">{{$t('settings.title')}}</span></v-toolbar-title><v-spacer></v-spacer>
<v-toolbar-items><v-btn @click.native="settings_dialog=false" text>{{$t('close')}}<v-icon right>close</v-icon></v-btn></v-toolbar-items>
</v-toolbar>
<Settings/>
</v-card>
</v-dialog>
<v-dialog :fullscreen="$vuetify.breakpoint.smAndDown" max-width="800px" v-model="contact_dialog">
<template v-slot:activator="{ on, attrs }">
<v-btn small dark text v-on="on" v-bind="attrs">
<v-icon small left>mail</v-icon><span>{{$t('contact.title')}}</span>
</v-btn>
</template>
<v-card>
<v-toolbar elevation="0" dark color="primary">
<v-toolbar-title><span role="heading" aria-level="1">{{$t('contact.title')}}</span></v-toolbar-title><v-spacer></v-spacer>
<v-toolbar-items><v-btn @click.native="contact_dialog=false" text>{{$t('close')}}<v-icon right>close</v-icon></v-btn></v-toolbar-items>
</v-toolbar>
<Contact @close="contact_dialog = false"/>
</v-card>
</v-dialog>
</span>
</div>
</template>
<script>
import Help from './Help.vue'
import Contact from './Contact.vue'
import Settings from './Settings.vue'
export default {
name: "FooterMenu",
components: {
Help,
Contact,
Settings
},
data: function() {
return {
contact_dialog: false,
help_dialog: false,
settings_dialog: false
}
}
}
</script>
\ No newline at end of file
<!-- eslint-disable -->
<template>
<div>
<h2 class="article_header">{{header_text}}</h2>
{{group_list}}
<details :title="inflect_tooltip" @toggle="toggle()" v-if="group_list" :class="$vuetify.breakpoint.name">
<summary :class="dictionary" onclick="this.blur()">bøying</summary>
<div class="inflection-canvas">
<inflectionTable :lemmaList="lemmas_with_word_class_and_lang" :mq="$vuetify.breakpoint.name" />
</div>
</details>
<span class="dict-label">{{dict_label}}</span>
<div class="header">
<router-link v-if="$route.name != 'lookup'"
:id="title_id"
:to="$parent.link_to_self.ref"
@click.native="heading_click"
v-bind:class="{'long_lemma': long_lemma}"
class="article_header">
<HeaderTitle :lemma_groups="lemma_groups" :secondary_header_text="secondary_header_text"/>
</router-link>
<span v-else>
<HeaderTitle :lemma_groups="lemma_groups" :secondary_header_text="secondary_header_text"/>
</span>
<InflectionButton :lemmas="$parent.article.lemmas" :dictionary="dictionary" :article_id="$parent.article.article_id"/>
<SplitInf v-if="!$parent.collapsed" :lemmas="lemmas"/>
<p v-if="!lemma_groups[1] && $parent.collapsed && $parent.snippet && $parent.has_content" v-bind:class="{'under_inflection_button': !$store.state.inflectionExpanded}">
{{$parent.snippet}}
</p>
<span v-if="$parent.collapsable" >
<v-btn class="expand_icon" v-if="$parent.collapsed"
text
small
right
@click="$emit('toggle-collapse')">
{{$t('article.show')}}
<v-icon right>expand_more</v-icon></v-btn>
<v-btn class="expand_icon" v-else
text
small
@click="$emit('toggle-collapse')">
<v-icon small>expand_less</v-icon></v-btn>
</span>
</div>
</template>
<script>
/* eslint-disable */
import helpers from '../utils/helpers.js'
import inflectionTable from 'inflection-table'
import SplitInf from './SplitInf.vue'
import InflectionButton from './InflectionButton.vue'
import HeaderTitle from './HeaderTitle.vue'
export default {
name: 'Header',
props: {
lemmas: Array,
dictionary: String,
article_id: Number
article_id: Number,
title_id: String
},
computed: {
header_text: function() {
return this.lemmas.map(lemma => lemma.lemma).join(', ')
},
inflect_tooltip: function() {
return this.dictionary == 'bob' ? 'Klikk for å se bøyinger' : 'Klikk for å sjå bøyingar'
},
dict_label: function() {
return {
'bob': 'bokmålsordboka',
'nob': 'nynorskordboka'
}[this.dictionary] || ''
},
group_list: function() {
return helpers.group_list(this.lemmas, this.dictionary)
},
inflection_groups_by_lemma: function() {
let components = Object.keys(this.$options.components)
return this.lemmas.map(
function(lemma_){
let inflection_groups = lemma_.paradigm_info.reduce((acc, std) => Object.assign(acc, {[std.inflection_group]: []}), {})
lemma_.paradigm_info.forEach(std => inflection_groups[std.inflection_group].push(std))
return {
lemma: lemma_.lemma,
inflection_groups: Object.fromEntries(Object.entries(inflection_groups).filter(e => components.includes(e[0].replace('/', '_'))))
}
}
)
},
lemmas_with_word_class_and_lang: function() {
return this.lemmas.map(lemma => Object.assign({language: this.dictionary == 'bob' ? 'nob' : 'nno',
word_class: lemma.paradigm_info[0].inflection_group.split('_')[0]}, lemma))
data: function() {
return {
long_lemma: false
}
},
components: {
inflectionTable
components: {
SplitInf,
InflectionButton,
HeaderTitle
},
data: function() {
return {
inflect_reported: false
mounted: function() {
if (this.$el.scrollWidth > this.$el.offsetWidth) {
this.long_lemma = true
}
},
methods: {
toggle: function() {
if (! this.inflect_reported) {
this.$plausible.trackEvent('open inflection', {props: {article: `/${this.dictionary}/${this.article_id}/${this.lemmas[0].lemma}`}})
heading_click: function() {
this.$plausible.trackEvent('article head click', {props: {article: `${this.dictionary} ${this.article_id}`}})
this.$parent.details_click(this.$parent.link_to_self)
},
inflection_classes: function(lemmas) {
let inf_classes = new Set()
let ureg = false
lemmas.forEach((lemma, i) => {
if (lemma.inflection_class) inf_classes.add(lemma.inflection_class)
else ureg = true
})
if (inf_classes.size){
let class_array = Array.from(inf_classes).sort()
if (ureg) class_array.push("ureg.")
let class_list
if (class_array.length < 3) {
class_list = class_array.join(" og ")
}
else {
class_list = class_array.slice(0, -1).join(", ") + " og " + class_array[class_array.length -1]
}
return " ("+ class_list +")"
}
this.inflect_reported = true
}
},
computed: {
content_locale: function() {
return this.$parent.content_locale
},
lang_tag_locale: function() {
return this.$parent.lang_tag_locale
},
secondary_header_text: function() {
let a_forms = []
this.lemmas.forEach((lemma, i) => {
if (lemma.paradigm_info[0] && lemma.paradigm_info[0].inflection[1] && lemma.paradigm_info[0].inflection[1].tags[0] == 'Inf') {
let inf2 = lemma.paradigm_info[0].inflection[1].word_form
if (inf2 && inf2.length) {
a_forms.push(inf2)
}
}
});
return a_forms.join(', ')
},
hgno_arabic: function() {
let hgnos = []
this.lemmas.forEach(lemma => {
let hgint = parseInt(lemma.hgno)
if (hgint > 0) {
hgnos.push(hgint)
}
})
return hgnos
},
lemma_groups: function() {
let groups = [{lemmas: this.lemmas}]
try {
if (this.lemmas[0].paradigm_info[0].tags[0] == "DET" && this.lemmas[0].paradigm_info[0].tags.length > 1) {
groups = [{description: this.$t('tags.'+this.lemmas[0].paradigm_info[0].tags[0], this.content_locale), pos_group: ["Quant", "Dem", "Poss"].includes(this.lemmas[0].paradigm_info[0].tags[1]) ? this.$t('determiner.' + this.lemmas[0].paradigm_info[0].tags[1], this.content_locale) : '', lemmas: this.lemmas}]
}
else if (this.lemmas[0].paradigm_info[0].tags[0] == 'NOUN') {
let genus_map = {}
let self = this
this.lemmas.forEach(lemma =>{
let genera = new Set()
lemma.paradigm_info.forEach(paradigm => {
if (paradigm.tags[1]) {
genera.add(paradigm.tags[1])
}
})
let genus_description = ""
if (genera.size == 3) {
genus_description += self.$t('tags.Masc') + ', ' + self.$t('tags.Fem', self.content_locale) + self.$t('or') + self.$t('tags.Neuter', self.content_locale)
} else {
genus_description += Array.from(genera).map(code => self.$t('tags.'+code, self.content_locale)).sort().join(self.$t('or'))
}
if (genus_map[genus_description]) {
genus_map[genus_description].push(lemma)
}
else {
genus_map[genus_description] = [lemma]
}
})
groups = Object.keys(genus_map).map(key => {
return {description: self.$t('tags.NOUN', self.content_locale), pos_group: key, lemmas: genus_map[key], }
})
}
else if (this.lemmas[0].paradigm_info[0].tags[0] != 'EXPR') {
groups = [{description: this.$t('tags.'+this.lemmas[0].paradigm_info[0].tags[0], this.content_locale), lemmas: this.lemmas}]
}
groups.forEach((lemma_group, index) => {
groups[index]['inflection_classes'] = this.inflection_classes(lemma_group.lemmas)
})
} catch(error) {
console.log("lemma_groups",this.article_id, this.dictionary, '"'+error.message+'"')
this.$parent.invalid = true
//console.error(error)
}
return groups
},
}
}
......@@ -82,160 +181,108 @@ export default {
<style>
article (table, th, td) {
border: solid 1px;
}
summary {
width: 30em;
text-align: center;
}
article h2 {
padding: 4px;
.article h3 {
padding-top: 4px !important;
padding-bottom: 0px !important;
font-family: Inria Serif;
font-size: 30px;
}
.word-classification {
text-decoration: underline dashed;
.article .sm h3, .article .xs h3, .long_lemma h3 {
font-size: 1.3em;
}
.dict-label {
color: var(--v-primary-base) ;
font-weight: bold;
padding-left: 5px;
position: absolute;
top: 0px;
left: 15px;
font-variant-caps: all-small-caps;
.sm .long_lemma, .xs .long_lemma h3 {
font-size: 1em;
}
details {
margin-top: 10px;
position: relative;
.article h3 {
font-size: 1.5em;
}
details > summary {
position: relative;
max-width: 130px;
list-style: none;
border: solid 2px var(--v-primary-base);
border-radius: 30px;
padding-top: 6px;
padding-bottom: 6px;
padding-right: 10px;
color: var(--v-primary-base);
background-color: #ffffff;
cursor: pointer;
}
details[open] > summary {
box-shadow: 5px 5px 0px var(--v-primary-base)
}
details > summary:after {
content: "⌄";
font-weight: bold;
position: absolute;
right: 0;
top: 1px;
margin-right: 3px;
h3 a {
text-decoration: none !important;
color: var(--v-primary-base) !important;
}
details > summary.bob:before {
content: "Se ";
}
details > summary.nob:before {
content: "Sjå ";
}
details[open] > summary.bob:before, details[open] > summary.nob:before {
content: "Skjul ";
.article h1.secondary_header {
padding-top: 0px;
padding-bottom: 4px;
}
details[open] > summary:after {
content: "⌃";
font-weight: bold;
position: absolute;
right: 0;
top: 8px;
margin-right: 3px;
.info-button {
margin-left: 6px;
margin-right: -2px;
height: 10px;
width: 10px;
}
@keyframes open {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
.word-classification {
text-decoration: underline dashed;
}
details[open] summary ~ * {
animation: open 0.3s ease-in-out;
}
.inflection-canvas {
overflow-x: auto;
position: absolute;
z-index: 5;
background-color: rgba(255, 255, 255, 0);
max-width: 100vw;
padding-top: 10px;
}
details > summary::-webkit-details-marker {
div.lemma {
display: none;
}
.infl-wrapper {
background-color: #ffffff;
border: solid 1px;
color: var(--v-primary-base);
border-color: var(--v-primary-base);
border-radius: 30px;
padding: 30px;
width: min-content;
margin: 5px;
}
.context {
color: var(--v-primary-lighten4) !important;
.article_header {
overflow:auto;
word-wrap: normal;
text-decoration: none;
}
div.lemma {
display: none;
.hgno {
color: var(--v-text-base) !important;
font-family: unset;
font-size: 16px;
font-weight: normal;
}
table {
border-collapse: collapse;
margin-top: 5px;
.expand_icon {
justify-content: center;
position: absolute;
bottom: 0px;
left: 0px;
padding-right: 24px !important;
width: 100%;
border-bottom-right-radius: 28px;
border-bottom-left-radius: 28px;
}
th, td {
border: solid 1px;
border-color: var(--v-primary-base);
padding: 5px;
.subheader {
color: var(--v-text-base);
font-weight: normal;
font-style: italic;
font-size: 14px;
padding-right: 10px;
}
th {
background-color: var(--v-tertiary-darken1)
.inflection_classes {
font-style: normal;
}
.infl-label {
text-align: center;
vertical-align: top;
.header_group_list {
font-variant: all-small-caps;
font-style: normal;
font-size: 18px;
}
article:not(.righ_hand_column) .inflection-canvas {
left: -35px;
p.under_inflection_button {
padding-top:10px !important;
}
td.hilite {
background-color: var(--v-tertiary-base)
}
</style>
<template>
<span>
<span v-bind:class="{ 'lookup': $route.name=='lookup'}" v-for="(lemma_group, i) in lemma_groups" :key="i">
<h3>
<!--
--><span v-for="(lemma, index) in lemma_group.lemmas"
:key="index"><DefElement v-if="lemma.annotated_lemma" :body="lemma.annotated_lemma" tag="span"/><span v-else>{{lemma.lemma}}</span><!--
--><span v-if="lemma.hgno"
:aria-label="$t('accessibility.homograph') + parseInt(lemma.hgno)"
:title="$t('accessibility.homograph')+parseInt(lemma.hgno)"
class="hgno">{{" "+roman_hgno(lemma)}}</span><!--
--><span
class="title_comma"
v-if="lemma_group.lemmas[1] && index < lemma_group.lemmas.length-1">{{", "}}
</span>
</span>
</h3>
<h3 v-if="secondary_header_text">{{secondary_header_text}}</h3>
<span :lang="$parent.$parent.lang_tag_locale" v-if="lemma_group.description" class="subheader">
<span class="header_group_list">{{lemma_group.description}}</span>
{{lemma_group.pos_group}}
<span v-if="$store.state.showInflectionNo" class="inflection_classes">{{lemma_group.inflection_classes}}</span>
</span>
</span>
</span>
</template>
<script>
import helpers from '../utils/helpers.js'
import DefElement from './DefElement.vue'
export default {
name: 'HeaderTitle',
components: {
DefElement
},
props: {
lemma_groups: Array,
secondary_header_text: String
},
methods: {
roman_hgno: helpers.roman_hgno,
}
}
</script>
<template id="">
<div class='help' :class="$vuetify.breakpoint.name">
<div v-if="$store.state.currentLocale == 'nno'">
<p>Du søkjer ved å skrive inn ordet og trykkje på returtasten (<kbd>enter</kbd>) eller velje eit søkjeforslag i nedtrekksmenyen. Tast <kbd>Shift</kbd>+<kbd>7</kbd> dersom du vil hoppe tilbake til søkjefeltet etter å ha søkt.
Overskrifta i toppen av kvart søkjetreff er ei lenkje til ei ny artikkelside for det aktuelle ordet. Om du ikkje får meir enn eitt treff i kvar ordbok, viser heile artikkelen i resultatlista, og du treng ikkje trykkje på lenkja. Er det fleire treff, kan du òg sjå artikkelinnhaldet i resultatlista ved å trykkje på "Vis artikkel" i botnen av søkjetreffet. Sjå innstillingane nedanfor om du ønskjer at resultatlista alltid skal vise fullstendige artiklar.
</p><p>
Kva ordbok du får treff i, avheng av kva ordbok du har valt å søkje i, og om ordet blir brukt i begge skriftspråka. Det ligg per i dag ikkje inne koplingar mellom ordbøkene, men vi har utvikla ein meinte-du-funksjon, som vi håpar blir til hjelp for å finne dei riktige orda både i bokmål og nynorsk.</p>
<p>Du kan søkje direkte på faste uttrykk, t.d. <em>gi katten i</em>. Alternativt finn du uttrykka samla til slutt i artikkelen eller artiklane dei er knytte til. Når du skriv inn eit enkelt ord i søkjefeltet, viser nedtrekksmenyen òg dei faste uttrykka som ordet er ein del av.</p>
<h2>Søk med jokerteikn</h2>
<p>Dersom du er usikker på skrivemåten av eit ord eller ønskjer treff i meir
enn éin artikkel, kan du bruke <kbd>%</kbd> og <kbd>*</kbd> for å få treff på null, eitt eller fleire teikn.
Symbolet <kbd>_</kbd> erstattar berre eitt teikn. Du kan plassere jokerteikna fleire stader i søkjeordet.</p>
Døme:
<p>Søkjer du på «<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>», er dei første treffa i nedtrekksmenyen «arbeider» i <em>Bokmålsordboka</em> og «arbeidar» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=inter%25es%25ant&scope=ei">inter%es%ant</a>» gjev treff på «interessant» i begge ordbøkene. </p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>» gjev treff på «førskolelærer» i <em>Bokmålsordboka</em> og «førskolelærar/førskulelærar» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=ku__&scope=e">ku__</a>» gjev treff på ord på fire bokstavar som startar på «ku».</p>
<h2>Kombiner søkjeord</h2>
<p>Symbolet <kbd>|</kbd> gjer det mogleg å søkje med fleire søkjeord samtidig. Det er òg mogleg å kombinere søkjeord som inneheld jokerteikn.</p>
Døme:
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>» gjev treff på «kjærlighet» i <em>Bokmålsordboka</em> og «kjærleik» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=undervis_r%7Cl%C3%A6r_r&scope=ei">undervis_r|lær_r</a>» gjev treff på «underviser» og «lærer» i <em>Bokmålsordboka</em> og «undervisar» og «lærar» i <em>Nynorskordboka</em>.</p>
</div>
<div v-if="$store.state.currentLocale == 'nob'">
<p>Du søker ved å skrive inn ordet og trykke på returtasten (<kbd>enter</kbd>) eller velge et søkeforslag i nedtrekksmenyen. Tast <kbd>Shift</kbd>+<kbd>7</kbd> hvis du vil hoppe tilbake til søkefeltet etter å ha søkt.
Overskriften i toppen av hvert søketreff er en lenke til en ny artikkelside for det aktuelle ordet. Hvis du ikke får mer enn ett treff i hver ordbok, vises hele artikkelen i resultatlisten, og du trenger ikke trykke på lenken. Er det flere treff, kan du også vise artikkelinnholdet i resultatlisten ved å trykke på "Vis artikkel" i bunnen av søketreffet. Se innstillingene nedenfor hvis du ønsker at resultatlisten alltid skal vise fullstendige artikler.
</p><p>
Hvilken ordbok du får treff i, avhenger av hvilken ordbok du har valgt å søke i, og om ordet blir brukt i begge skriftspråkene. Det ligger per i dag ikke inne koblinger mellom ordbøkene, men vi har utviklet en mente-du-funksjon, som vi håper blir til hjelp for å finne de riktige ordene både i bokmål og nynorsk.
</p><p>Du kan søke direkte på faste uttrykk, f.eks. <em>gi katten i</em>. Alternativt finner du uttrykkene samlet til slutt i artikkelen eller artiklene de er knyttet til. Når du skriver inn et enkelt ord i søkefeltet, viser nedtrekksmenyen også de faste uttrykkene som ordet inngår i.</p>
<h2>Søk med jokertegn</h2>
<p>Dersom du er usikker på skrivemåten av et ord eller ønsker treff i mer enn én artikkel, kan du bruke <kbd>%</kbd> og <kbd>*</kbd> for å få treff på null, ett eller flere tegn.
Symbolet <kbd>_</kbd> erstatter kun ett tegn. Du kan plassere jokertegnene flere steder i søkeordet.</p>
Eksempler:
<p>Søker du på «<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>», er de første treffene i nedtrekksmenyen «arbeider» i <em>Bokmålsordboka</em> og «arbeidar» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=inter%25es%25ant&scope=ei">inter%es%ant</a>» gir treff på «interessant» i begge ordbøkene. </p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>» gir treff på «førskolelærer» i <em>Bokmålsordboka</em> og «førskolelærar/førskulelærar» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=ku__&scope=e">ku__</a>» gir treff på ord på fire bokstaver som begynner med «ku».</p>
<h2>Kombiner søkeord</h2>
<p>Symbolet <kbd>|</kbd> gjør det mulig å søke med flere søkeord samtidig. Det er også mulig å kombinere søkeord som inneholder jokertegn.</p>
Eksempler:
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>» gir treff på «kjærlighet» i <em>Bokmålsordboka</em> og «kjærleik» i <em>Nynorskordboka</em>.</p>
<p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=undervis_r%7Cl%C3%A6r_r&scope=ei">undervis_r|lær_r</a>» gir treff på «underviser» og «lærer» i <em>Bokmålsordboka</em> og «undervisar» og «lærar» i <em>Nynorskordboka</em>.</p>
</div>
<div v-if="$store.state.currentLocale == 'eng'">
<p>Search the website by entering a word, press <kbd>return</kbd> or pick a search suggestion in the drop-down menu. Press <kbd>/</kbd> whenever you want to jump back to the search box.
The heading of each search result is a link to a lookup page for the article in question. If you get no more than one result in each dictionary, the entire article is shown in the list of results, and you won't need to click the link. If there are more resutls, you may expand the article content in the results list by clicking "Show article" at the bottom of the result item. You can make the articles expand by default in the settings below.
</p><p>
The dictionary from which to get search results depends on the dictionary selected, and if the word is used in both written languages. Currently, the two dictionaries are not linked at word level. Thus a ‘Did you mean’ function has been developed. The hope is that this function will be helpful in finding the right words in both Bokmål and Nynorsk. It is possible to search for fixed expressions, e.g.
<em>gi katten i</em> (not give a damn). It is also possible to find such expressions at the end of each article or the articles they are linked to. When typing a single word in the search field, the drop-down menu also shows the common expressions the word is part of.
</p>
<h2>Wildcard search</h2>
<p>If unsure of the spelling of a word or want results from more than one article, it is possible to use <kbd>%</kbd> and <kbd>*</kbd> to replace zero, one or more characters.
Underscore (<kbd>_</kbd>) replaces only one character. You may use the wilcard symbols multiple times in your search expression.</p>
Examples:
<p>The search query “<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>”, will result in results in the drop-down menu with “arbeider” (worker) in the Bokmål dictionary and “arbeidar” (worker) in the Nynorsk Dictionary.</p>
<p>The search query “<a href="https://ordbokene.no/bm,nn/search?q=inter%25es%25ant&scope=ei">inter%es%ant</a>” returns the result “interessant” (interesting) in both dictionaries. </p>
<p>The search query “<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>” gives resutls for “førskolelærer” (pre-school teacher) in the Bokmål dictionary and “førskolelærar/førskulelærar” (pre-school teacher) in the Nynorsk dictionary.</p>
<p>The search query «<a href="https://ordbokene.no/bm,nn/search?q=ku__&scope=e">ku__</a>» returns four letter words starting with «ku».</p>
<h2>Combine search terms</h2>
<p>The symbol <kbd>|</kbd> allows you to use in several search words at the same time. You can also combine search words that contain wildcard symbols.</p>
Examples:
<p>The search “<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>” yields the result “kjærlighet” in the Bokmål dictionary and “kjærleik” (love) in the Nynorsk dictionary.</p>
<p>The search “<a href="https://ordbokene.no/bm,nn/search?q=undervis_r%7Cl%C3%A6r_r&scope=ei">undervis_r|lær_r</a>” results in “underviser” (teacher) and “lærer” (teacher) in the Bokmål dictionary and “undervisar” and “lærar” in the Nynorsk dictionary.</p>
</div>
<div v-if="$store.state.currentLocale == 'ukr'">
<p>Для пошуку введіть слово і натисніть кнопку <kbd>enter</kbd> або виберіть варіант пошуку у спадному меню. Натисніть <kbd>Shift</kbd>+<kbd>7</kbd>, якщо ви хочете повернутися до поля пошуку після виконання пошуку.
Заголовок у верхній частині кожного результату пошуку - це посилання на сторінку нової статті про це слово. Якщо ви не знайдете більше одного збігу в кожному словнику, вся стаття з’явиться у списку результатів, і вам не потрібно переходити за посиланням. Якщо результатів декілька, ви також можете переглянути вміст статті у списку результатів, натиснувши "показати статтю" внизу результату пошуку. Дивіться налаштування нижче, якщо ви хочете, щоб у списку результатів завжди випадали повні статті.
</p>
<p>
У якому словнику ви отримаєте збіги, залежить від того, який словник ви вибрали для пошуку,
і чи використовується це слово в обох письмових мовах. Наразі між словниками немає посилань,
але ми розробили для вас функцію, яка, сподіваємось, допоможе знайти потрібні слова як у <em>букмолі</em>,
так і в <em>нюношку</em>.
</p>
<p>
Ви можете здійснювати пошук безпосередньо за фіксованими виразами,
напр., <em>gi katten i</em> (не заморочуватись нічим або ніким).
Крім того, ви знайдете вирази разом у статті чи статтях, з якими вони пов’язані.
Коли ви вводите в поле пошуку одне слово, у спадному меню також показуються фіксовані вирази, до
яких входить це слово.
</p>
<h2>Пошук за допомогою символів підстановки</h2>
<p>Якщо ви не впевнені в написанні слова або хочете знайти збіги в більш ніж одній статті, ви можете використовувати <kbd>%</kbd> і <kbd>*</kbd> щоб отримати збіги від нуля, одного або декількох символів.
Символ <kbd>_</kbd> замінює лише один символ. Ви можете розмістити символи підстановки в декількох місцях ключового слова.</p>
Приклади:
<p>Пошук за запитом “<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>” покаже у спадному меню результатів слова “arbeider” (працівник) у <em>Словнику букмола</em> і “arbeidar” (працівник) у <em>Словнику нюношка</em>.</p>
<p>Пошук “<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>” надає результати за словом “førskolelærer” (вчитель дошкільної освіти) у <em>Словнику букмола</em> та “førskolelærar/førskulelærar” (вчитель дошкільної освіти) у <em>Словнику нюношка</em>.</p>
<p>Пошук “<a href="https://ordbokene.no/bm,nn/search?q=ku__&scope=e">ku__</a>” забезпечує відповідність словами з чотирьох букв, що починаються на “ku”.</p>
<h2>Поєднайте ключові слова</h2>
<p>Символ <kbd>|</kbd> дозволяє здійснювати пошук за кількома ключовими словами одночасно. Також можливо комбінувати ключові слова, що містять символи підстановки.</p>
Приклади:
<p>Пошук “<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>” надає результати за словом “kjærlighet” (любов) у <em>Словнику букмола</em> і “kjærleik” (любов) у <em>Словнику нюношка</em>.</p>
<p>Пошук “<a href="https://ordbokene.no/bm,nn/search?q=undervis_r%7Cl%C3%A6r_r&scope=ei">undervis_r|lær_r</a>” надає результати за словом “underviser” (навчання) та “lærer” (вчитель) у <em>Словнику букмола</em> і “undervisar” (навчання) та “lærar” (вчитель) у <em>Словнику нюношка</em>.</p>
</div>
</div>
</template>
<style scoped>
ul.bullet > li {
list-style: disc;
margin-left: 20px;
}
a {
text-decoration: none;
}
blockquote {
margin-left:50px;
}
kbd {
border-radius: 5px !important;
background-color: var(--v-tertiary-darken1) !important;
color: var(--v-text-base) !important;
}
</style>
<!-- eslint-disable -->
<template>
<span :is="$vuetify.breakpoint.smAndDown ? 'div' : 'span'" class="inflection-wrapper" v-if="inflected">
<v-btn :lang="$parent.$parent.lang_tag_locale" :aria-controls="'inflection-canvas'+$parent.title_id" :aria-expanded="inflection_expanded ? 'true' : 'false'" v-if="!($store.state.inflectionExpanded && $route.name)" class="show-inflection" small depressed rounded @click.native="toggle" :class="$vuetify.breakpoint.name">
<span :lang="$parent.lang_tag_locale">{{inflection_expanded? $t('article.hide_inflection', $parent.content_locale):$t('article.show_inflection', $parent.content_locale)}}</span><span class = "inflection-arrow"><v-icon small right>{{this.inflection_expanded? "remove" : "add"}}</v-icon></span>
</v-btn>
<div :id="'inflection-canvas'+$parent.title_id" class="inflection-canvas">
<inflectionTable v-if="inflection_expanded || always_expand" :eng="$store.state.currentLocale == 'eng'" :ukr="$store.state.currentLocale == 'ukr'" :lemmaList="lemmas_with_word_class_and_lang" :mq="$vuetify.breakpoint.name" :context="$store.state.inflectionTableContext" :key="$store.state.currentLocale + $store.state.inflectionTableContext"/>
</div>
</span>
</template>
<script>
/* eslint-disable */
import inflectionTable from 'inflection-table'
export default {
name: 'InflectionButton',
props: {
lemmas: Array,
dictionary: String,
article_id: Number
},
computed: {
lemmas_with_word_class_and_lang: function() {
return this.lemmas.map(lemma => Object.assign({language: this.dictionary == 'bm' ? 'nob' : 'nno',
word_class: lemma.paradigm_info[0].inflection_group.split('_')[0]}, lemma))
},
inflected: function() {
return this.lemmas.reduce((acc, lemma) => acc + lemma.paradigm_info.reduce((acc2, digm) => digm.inflection_group.includes("uninfl") ? acc2 : acc2 + digm.inflection.length, 0), 0) > this.lemmas.length
},
always_expand: function() {
return !this.$parent.$parent.collapsed && this.$store.state.inflectionExpanded && this.$route.name
}
},
components: {
inflectionTable
},
data: function() {
return {
inflection_expanded: false
}
},
methods: {
toggle: function() {
if (!this.inflection_expanded) {
this.$plausible.trackEvent('inflection click', {props: {article: `${this.dictionary} ${this.article_id}`}})
}
this.inflection_expanded = !this.inflection_expanded
}
},
}
</script>
<style>
.show-inflection .v-icon {
color: var(--v-primary-base) !important;
margin-left: 4px !important;
}
@keyframes open {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
div.show-inflection {
padding-top: 10px;
}
.inflection-canvas {
overflow-x: auto;
/*position: absolute;*/
z-index: 5;
background-color: rgba(255, 255, 255, 0);
/*max-width: 100vw;*/
}
.inflection-canvas > div {
padding-top: 10px;
padding-bottom: 10px;
}
.inflection-wrapper {
color: var(--v-text-base);
font-size: 14px;
margin-top: 10px;
}
.context {
color: #545454 !important;
}
div.lemma {
display: none;
}
table {
border-collapse: collapse;
margin-top: 5px;
}
th, td {
border: solid 2px var(--v-button-darken1);
padding: 5px;
}
th {
background-color: var(--v-button-base);
}
.infl-label {
text-align: center;
vertical-align: top;
}
td.hilite {
background-color: var(--v-tertiary-base);
text-align: center
}
</style>
<template>
<v-card>
<v-list>
<v-list-group prepend-icon="help">
<template v-slot:activator>
<v-list-item-title>{{$t('menu.help')}}</v-list-item-title>
</template>
<v-list-item><Help/></v-list-item>
</v-list-group>
<v-list-item link to="/om" v-on:click="$emit('close')">
<v-list-item-icon><v-icon>info</v-icon></v-list-item-icon>
<v-list-item-title>{{$t('menu.about')}}</v-list-item-title>
<v-list-item-icon><v-icon>chevron_right</v-icon></v-list-item-icon>
</v-list-item>
<v-list-group sub prepend-icon="language">
<template v-slot:activator>
<v-list-item-title>{{$t('settings.locale.title')}}</v-list-item-title>
</template>
<v-list-item>
<v-list subheader>
<v-radio-group v-model="updateLocale" :label="$t('settings.locale.description')">
<v-radio value="nob" label="Bokmål (nob)"></v-radio>
<v-radio value="nno" label="Nynorsk (nno)"></v-radio>
<v-radio value="eng" label="English (eng)"></v-radio>
<v-radio value="ukr" label="Українська (ukr)"></v-radio>
</v-radio-group>
</v-list></v-list-item>
</v-list-group>
<v-list-group v-if="false" prepend-icon="book">
<template v-slot:activator>
<v-list-item-title>{{$t('settings.dict.title')}}</v-list-item-title>
</template>
<v-list-item>
<v-list subheader>
<v-radio-group mandatory v-model="updateDefaultDict" :label="$t('settings.dict.description')">
<v-radio value="bm,nn" :label="$t('dicts.bm,nn')"></v-radio>
<v-radio value="bm" :label="$t('dicts.bm')"></v-radio>
<v-radio value="nn" :label="$t('dicts.nn')"></v-radio>
</v-radio-group>
</v-list></v-list-item>
</v-list-group>
<v-list-group prepend-icon="settings">
<template v-slot:activator>
<v-list-item-title>{{$t('settings.title')}}</v-list-item-title>
</template>
<v-list-item><Settings/></v-list-item>
</v-list-group>
<v-list-item v-if="!$store.state.unavailable" v-on:click='$store.commit("resetStore")'>
<v-list-item-icon><v-icon>delete</v-icon></v-list-item-icon>
<v-list-item-title>{{$t("settings.reset")}}</v-list-item-title>
</v-list-item>
<v-list-group prepend-icon="mail">
<template v-slot:activator>
<v-list-item-title>{{$t('contact.title')}}</v-list-item-title>
</template>
<v-list-item><Contact @close="$emit('close')"/></v-list-item>
</v-list-group>
</v-list>
</v-card>
</template>
<script>
import Settings from './Settings.vue'
import Help from './Help.vue'
import Contact from './Contact.vue'
export default {
name: "Menu",
components: {
Settings,
Help,
Contact
},
computed: {
updateLocale: {
get () { return this.$store.state.currentLocale},
set(value) {
this.$store.commit("setLocale", {value: value, i18n: this._i18n})
}
},
updateDefaultDict: {
get () { return this.$store.state.defaultDict},
set(value) {
this.$store.commit("setDefaultDict", value)
}
}
},
}
</script>
<template>
<div v-bind:class="{'centered': bottom}">
<span color="tertiary" class = "pagination">
<v-pagination @input="update" v-model="$parent.$parent.page" :class="$vuetify.breakpoint.name" :total-visible="$vuetify.breakpoint.smAndDown && !bottom ? 5 : 8" circle :length="Math.ceil(Math.max($parent.count_bm, $parent.count_nn)/$parent.$parent.perPage)"></v-pagination>
<span class="result-counts" v-if="!bottom && $vuetify.breakpoint.smAndDown">
<span class="total-results">{{$parent.$parent.total_results()}} treff </span>
<span v-if="$parent.$parent.lang=='bm,nn'" class="dict-counts"> | {{$vuetify.breakpoint.mdAndDown? 'bokmål': 'Bokmålsordboka'}}: {{$parent.count_bm}} | {{$vuetify.breakpoint.mdAndDown? 'nynorsk': 'Nynorskordboka'}}: {{$parent.count_nn}}</span>
<span v-if="$parent.$parent.lang!='bm,nn'" class="dict-counts"> | {{$t('dicts_inline.'+$parent.lang)}}</span>
</span>
</span>
<v-btn v-if="bottom && $parent.$parent.total_results() > 6" text @click.native="to_top"><v-icon left>expand_less</v-icon>{{$t('to_top')}}</v-btn>
</div>
</template>
<script>
export default ({
name: "Pagination",
props: {
bottom: Boolean
},
methods: {
update: function(item) {
if (this.bottom) {
this.to_top()
}
this.$emit('update-page', item)
},
to_top: function() {
let first = document.getElementById("result0")
if (first) first.focus();
window.scrollTo(0,0)
}
},
})
</script>
<style scoped>
.centered {
text-align: center;
padding-bottom: 10px;
}
.centered > span {
justify-content: center;
padding-bottom: 10px
}
</style>
\ No newline at end of file
<template>
<div :class="$vuetify.breakpoint.name">
<form @submit.prevent="submit">
<v-combobox
v-model="select"
prepend-inner-icon="search"
autocapitalize = 'off'
:loading="loading"
:items="items"
append-icon=""
:search-input.sync="search"
item-text="match"
:menu-props="{maxHeight: $vuetify.breakpoint.name === 'xs' ? 190 : 500, transition: 'fade-transition', allowOverflow: true}"
return-object
rounded
clearable
:autofocus="!$route.hash && !$store.state.noMouse && $vuetify.breakpoint.mdAndUp"
hide-no-data
no-filter
auto-select-first
hide-details
:label="$t('search_placeholder') + $t(`dicts.${$parent.lang}`)"
:aria-label="$t('search_placeholder') + $t(`dicts.${$parent.lang}`)"
solo
full-width
:placeholder="$t('search_placeholder') + $t(`dicts.${$parent.lang}`)"
ref="autocomplete"
color="primary"
:dense="$vuetify.breakpoint.mdAndDown"
>
<template v-slot:prepend-inner>
<v-menu allowOverflow offsetY v-model="dictMenuOpened">
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs"
v-on="on"
plain
depressed
color = "primary"
text
@click.native="items=[]">
<v-icon left>book</v-icon>
<span v-if="$vuetify.breakpoint.mdAndUp">
{{$t(`dicts.${$parent.lang}`)}}
<v-icon right>{{ dictMenuOpened? 'expand_less' : 'expand_more'}}</v-icon>
</span><span v-if="$vuetify.breakpoint.smAndDown">{{$t(`dicts_short.${$parent.lang}`)}}</span></v-btn>
</template>
<v-list>
<v-list-item v-for="item in ['bm,nn','bm','nn'].map(l => {return {label: $t(`dicts.${l}`), tag: l}})" :key="item.tag"
active-class="v-list-item--active" @click="update_lang_form(item.tag)">
<v-list-item-icon v-if="$parent.lang == item.tag"><v-icon color="primary">radio_button_checked</v-icon></v-list-item-icon>
<v-list-item-icon v-else><v-icon>radio_button_unchecked</v-icon></v-list-item-icon>
<v-list-item-title >{{ item.label }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-divider vertical aria-hidden="true"/><v-icon right>search</v-icon>
</template>
<template v-slot:item="data">
<span v-if="data.item.type == 'empty' || data.item.type == 0">
Søk: <strong>{{data.item.match}}</strong> </span>
<span v-if="data.item.type == 'exact'">
<span class="searchLemma">
<strong>{{data.item.match}}</strong>
</span>
<span class="dict-parentheses" v-if="(get_lang()=='bm,nn')">
({{["bokmål","nynorsk","bokmål, nynorsk"][data.item.lang-1]}})
</span>
</span>
<span v-if="data.item.type == 'other'">
{{data.item.match}} </span>
</template>
</v-combobox>
<SearchToolbar @updatePos="update_pos"
@updateScope="update_scope" />
</form>
</div>
</template>
<script>
import SearchToolbar from './SearchToolbar.vue'
export default {
data: function() {
return {
loading: false,
items: [],
search: null,
select: this.$parent.queryString || null,
suggesting: null,
menuDialog: false,
dictMenuOpened: false
}
},
components: {
SearchToolbar
},
watch: {
search (val) {
const time = Date.now()
if (! val) {
this.items = []
} else {
this.run_query(val, time)
}
},
select(item) {
if (item) {
if (typeof item != 'string') {
this.items = []
this.suggesting = false
this.submit(item)
}
}
},
$route (to, from) {
this.select = to.query.q || ""
}
},
methods: {
hotkey(e) {
if (e.key !== "/") return;
if (this.$store.state.disableHotkey) return
if (e.altKey || e.ctrlKey || e.metaKey) return;
if (/^(?:input|textarea|select)$/i.test(e.target.tagName)) return;
e.preventDefault()
this.$refs.autocomplete.$refs.input.focus()
},
update_lang_form(lang) {
this.$emit('update-lang-form', lang)
// Submit if switching on front page
if (this.search && this.search!=this.$parent.queryString && !this.items[0]) {
this.submit({q: this.search, match: this.search, time: Date.now()})
}
else {
this.$parent.reload_params()
}
},
update_scope(scope) {
this.$emit('updateScope', scope)
},
update_pos(pos) {
this.$emit('updatePos', pos)
},
get_lang() {
return this.$parent.lang
},
run_query(q, time) {
q = q.trim()
if (this.items[0]) {
if (this.items[0].time < time) {
// Whitespace necessary in case option already exists in dropdown
this.items.splice(0,1, {q, match: q, time, type: 0})
}
}
else {
this.items.push({q, match: q, time, type: 0})
}
let words = q.split(/ |\|/)
if (words.length > 20) {
this.items = [{q, match: q, time, type: 0}]
return
}
for (let i = 0; i < words.length; i++) {
if (words[i].length > 40) {
this.items = [{q, match: q, time, type: 0}]
return
}
}
this.suggesting = true
let self = this
let params = {q, dict: self.get_lang(), n: 20, dform: 'int', meta: 'n', include: this.$parent.scope, wc: self.$parent.pos_selected}
this.$parent.api.get('api/suggest?', {params})
.then(async (response) => {
if (self.$refs.autocomplete.searchInput == q & self.suggesting) {
let suggestions = []
if (response.data.a.exact) {
suggestions = response.data.a.exact.map(item => ({q: q, match: item[0], time: time, lang: [item[1]], type: "exact"}))
}
if (response.data.a.inflect) {
let inf = response.data.a.inflect.map(item => ({q: q, match: q, inflected: item[0], time: time, type: "other"}))
suggestions = suggestions.concat(inf)
}
if (response.data.a.freetext) {
let fr = response.data.a.freetext.map(item => ({q: q, match: item[0], time: time, type: "other"}))
suggestions = suggestions.concat(fr)
}
if (suggestions.length) {
if (suggestions[0].q.toLowerCase() != suggestions[0].match.toLowerCase()) {
suggestions.unshift({q, match: q, time, type: "empty"})
}
self.items = suggestions
}
else {
self.items = [{q, match: q, time, type: "empty"}]
}
}
})
},
submit(item) {
if (!item.q) {
item = this.items[0] || {q: this.search, match: this.search, time: Date.now() }
this.items = []
}
this.$emit('submit', item)
let self = this
setTimeout(() => {
if (!self.$store.state.noMouse && this.$vuetify.breakpoint.mdAndUp) self.$refs.autocomplete.$refs.input.select()
else self.$refs.autocomplete.$refs.input.blur()
self.suggesting = false
}, 1)
}
},
mounted: function() {
document.addEventListener("keydown", this.hotkey);
}
}
</script>
<style scoped>
.searchResult {
margin-right: 5px;
}
.searchLemma {
color: var(--v-primary-base);
}
form {
padding-left: 10px;
padding-right: 10px;
padding-top: 10px;
}
.accessibility_link {
display: inline;
position: absolute;
left: -10000px;
}
.accessibility_link:focus {
position: absolute;
left: 48px;
top : 48px;
background-color: white;
padding: 10px;
z-index: 10000;
}
</style>
<template>
<section id="search_results">
<div class="flex-container" :class="$vuetify.breakpoint.name">
<ul class="hits" v-if="results_bob.length">
<li class="article_container" v-for="(result, index) in results_bob" :key="index + results_hash">
<section id="search_results" :class="$vuetify.breakpoint.name">
<div v-if="show_per_page" class="pagination-container">
<Pagination v-if="show_pagination" @update-page="$emit('update-page')"/>
<span id="per-page">
<span id="per-page-label">{{$t('per_page')}}</span>
<v-select class="per-page-select"
aria-labelledby="per-page-label"
v-model="$parent.perPage"
:items="[10, 20, 50, 100]"
@input="$parent.update_per_page"
hide-details
dense
:menu-props="{ offsetY: true }"
append-icon="expand_more"/>
</span>
</div>
<div class="flex-container" v-if="!$parent.waiting" :class="$vuetify.breakpoint.name">
<div class="hits" v-if="$vuetify.breakpoint.smAndDown">
<div class="article_container" v-for="(result, index) in results_bm.concat(results_nn)" :key="index + both_hash"
:style="'order:'+(index < results_bm.length? 2*index : ((index-results_bm.length)*2) + 1)">
<Article
:article="result"
:title_id="`result${index}`"
:queryPattern="queryPattern"
:scope="scope"
@article-click="article_link_click"
@details-click="details_click">
</Article>
</div>
</div>
<div class="hits" v-if="$vuetify.breakpoint.mdAndUp && lang!= 'nn'" v-bind:class="{'wide': lang=='bm'}">
<div class="dict-label-top"> <span role="heading" aria-level="2" class="dict-label-title"> bokmålsordboka </span><span v-if="count_bm != 1"> | <span v-if="count_bm == 0">{{$t('notifications.no_results')}}</span><span v-else>{{count_bm}} {{$t('notifications.results')}}</span></span></div>
<div class="article_container" v-for="(result, index) in results_bm" :key="index + bm_hash">
<Article
:article="result"
:title_id="`result${index}`"
:queryPattern="queryPattern"
:scope="scope"
@article-click="article_link_click"
@details-click="details_click">
</Article>
</li>
</ul>
<ul class="hits" v-if="results_nob.length">
<li class="article_container" v-for="(result, index) in results_nob" :key="index + results_hash">
</div>
</div>
<div class="hits" v-if="$vuetify.breakpoint.mdAndUp && lang!='bm'" v-bind:class="{'wide': lang=='nn'}">
<div class="dict-label-top"> <span role="heading" aria-level="2" class="dict-label-title"> nynorskordboka </span><span v-if="count_nn != 1"> | <span v-if="count_nn == 0">{{$t('notifications.no_results')}}</span><span v-else>{{count_nn}} {{$t('notifications.results')}}</span></span></div>
<div class="article_container" v-for="(result, index) in results_nn" :key="index + nn_hash">
<Article
:article="result"
:title_id="`result${index + results_bm.length}`"
:queryPattern="queryPattern"
:scope="scope"
@article-click="article_link_click"
@details-click="details_click"
:class="right_col_class_name">
@details-click="details_click">
</Article>
</li>
</ul>
</div>
</div>
</div>
<Pagination v-if="show_pagination && !$parent.waiting" @update-page="$emit('update-page')" bottom/>
<div>
</div>
</section>
</template>
<script>
import Article from './Article.vue'
import Pagination from './Pagination.vue'
export default {
name: 'SearchResults',
props: {
hits: Array,
lang: String
results_bm: Array,
results_nn: Array,
lang: String,
meta: Object,
queryPattern: String,
scope: String
},
computed: {
right_col_class_name: function() {
if (this.$vuetify.breakpoint.mdAndUp) {
if (this.results_bob.length) {
return 'righ_hand_column'
}
both_hash: function(){
return this.results_bm.concat(this.results_nn).reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
},
bm_hash: function(){
return this.results_bm.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
},
nn_hash: function(){
return this.results_nn.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
},
count_bm: function(){
if (this.$parent.article_info.articles.bm ){
return this.$parent.article_info.articles.bm.length
}
else {
return 0
}
return ''
},
results_bob: function(){
return this.hits.filter(hit => hit.dictionary == 'bob')
count_nn: function(){
if (this.$parent.article_info.articles.nn ){
return this.$parent.article_info.articles.nn.length
}
else {
return 0
}
},
results_nob: function(){
return this.hits.filter(hit => hit.dictionary == 'nob')
show_pagination: function() {
return !this.$parent.article && this.$parent.article_info && (this.count_bm > this.$parent.perPage || this.count_nn > this.$parent.perPage)
},
results_hash: function(){
return this.hits.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
}
show_per_page: function() {
return this.$parent.article_info && (this.count_bm > 10 || this.count_nn > 10) && !this.$parent.article
},
},
methods: {
article_link_click: function(item) {
......@@ -58,76 +121,118 @@ export default {
},
details_click: function(item) {
this.$emit('details-click', item)
}
},
},
components: {
Article
Article,
Pagination
}
}
</script>
<style >
h2 {
<style>
.article h3 {
color: var(--v-primary-base);
margin: 0px;
padding: 20px 0px 20px 0px;
}
.hits {
margin-top:0px;
}
.flex-container {
padding-top: 0px;
display: flex;
}
.flex-container.xs, .flex-container.sm {
flex-direction: column;
.flex-container.sm, .flex-container.xs {
padding-top: 0px;
}
.flex-container > ul {
padding: 0px !important;
.hits {
min-width: 50%;
}
#search_results details.xs, #search_results details.sm {
display: none;
.hits.wide {
min-width: 50%;
}
#search_results .article_content.xs, #search_results .article_content.sm {
position: relative;
height: 100px;
overflow: hidden;
.flex-container.sm .hits, .flex-container.xs .hits {
min-width: 100%;
display: flex;
flex-direction: column;
}
.results-count {
color: var(--v-primary-base);
margin-left: 24px;
#search_results .article_content.xs > section, #search_results .article_content.sm > section {
display: none;
}
#search_results .article_content.xs h3, #search_results .article_content.sm h3 {
display: none;
}
.pagination {
display: flex;
flex-wrap: wrap;
#search_results .article_content.xs > section.definitions, #search_results .article_content.sm > section.definitions {
display: block;
}
#search_results .article_content.xs > .fade, #search_results .article_content.sm > .fade {
display: inline-block;
text-align: right;
font-size: 20px;
.total-results {
font-weight: bold;
color: var(--v-primary-base);
padding-top: 30px;
width:100%;
height:60px;
position:absolute;
left:0;
top:40px;
background:linear-gradient(rgba(255, 255, 255, 0) 0px, var(--v-tertiary-base));
}
article.righ_hand_column div.inflection-wrapper {
right: -35px;
.result-counts {
padding: 10px;
justify-content: right !important;
}
.total-results, .dict-counts {
white-space: nowrap;
}
.pagination ul {
padding-inline-start: 0px !important;
justify-content: right;
}
#per-page {
padding-bottom: 10px;
padding-right: 10px;
margin-left: 10px;
display: flex;
flex-direction: row;
gap: 10px;
align-items: baseline;
}
.per-page-select {
max-width: 64px;;
}
.pagination-container {
margin-left: 10px;
padding-bottom: 10px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.lg .pagination-container, .xl .pagination-container {
padding-top: 10px;
}
.dict-label-title {
color: var(--v-primary-base) ;
font-weight: bold;
font-size: 24px;
font-variant-caps: all-small-caps;
}
.dict-label-top {
padding: 10px;
padding-left: 24px;
}
</style>
<template>
<div id="search_toolbar" :class="$vuetify.breakpoint">
<v-card v-if="$vuetify.breakpoint.smAndDown" rounded="xl" min-height="38px">
<v-list two-line v-if="expanded" id="settings-list">
<v-list-item>
<v-select
v-model="$parent.$parent.pos_selected"
hide-details
:menu-props="{ offsetY: true }"
:aria-label="$t('options.pos')"
:items="pos_items"
append-icon="expand_more"
@input="updatePos">
</v-select>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="fulltext"
hide-details
:label="$t('options.fulltext')"/>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="inflected_results"
hide-details=""
:label="$t('options.inflected')"/>
</v-list-item>
<v-list-item v-if="($route.name == 'search' || $route.name == 'word') && $store.state.collapseArticles != 'auto'">
<v-switch hide-details dense v-model="updateCollapse" :label="$t('options.collapse')"></v-switch>
</v-list-item>
</v-list>
<v-btn v-if="$vuetify.breakpoint.smAndDown"
class="search_options"
v-bind:class="{'expanded': expanded}"
height="38px"
rounded
text
@click="expanded=!expanded"
:aria-expanded="expanded ? 'true' : 'false'"
aria-controls="settings-list">
{{expanded? '' : $t('options.title')}}<v-icon small color="primary" :right="!expanded">{{expanded ? 'expand_less': 'expand_more'}}</v-icon></v-btn>
</v-card>
<div id="desktop_toolbar" v-else>
<span>
<v-checkbox
v-model="inflected_results"
dense
hide-details
:label="$t('options.inflected')"/>
</span>
<span>
<v-checkbox
v-model="fulltext"
dense
hide-details
:label="$t('options.fulltext')"/>
</span>
<span id="toolbar-pos-select">
<v-select
v-model="$parent.$parent.pos_selected"
hide-details
:menu-props="{ offsetY: true }"
dense
:aria-label="$t('options.pos')"
:items="pos_items"
append-icon="expand_more"
@input="updatePos">
</v-select>
</span>
<span id="display-options" v-if="($route.name == 'search' || $route.name == 'word') && $store.state.collapseArticles != 'auto'">
<v-switch v-model="updateCollapse" hide-details dense :label="$t('options.collapse')"></v-switch>
</span>
</div>
</div>
</template>
<script>
export default {
name: "SearchToolbar",
data: function() {
return {
expanded: false
}
},
computed: {
updateCollapse: {
get () { return this.$store.state.collapseArticles != "never"},
set(value) {
if (value) this.$store.commit("setCollapse", "always")
else this.$store.commit("setCollapse", "never")
}
},
inflected_results: {
get() {
return this.$parent.$parent.scope.includes("i")
},
set() {
if (this.$parent.$parent.scope.includes("i")) {
this.$emit('updateScope', this.$parent.$parent.scope.replace("i",""))
}
else {
this.$emit('updateScope', this.$parent.$parent.scope + "i")
}
}
},
fulltext: {
get() {
return this.$parent.$parent.scope.includes("f")
},
set() {
if (this.$parent.$parent.scope.includes("f")) {
this.$emit('updateScope', this.$parent.$parent.scope.replace("f",""))
}
else {
this.$emit('updateScope', this.$parent.$parent.scope + "f")
}
}
},
pos_items: function() {
let items = ["VERB", "NOUN", "ADJ", "PRON", "DET", "ADV", "ADP", "CCONJ", "SCONJ", "INTJ"].map(tag =>{
return {text: this.$t("pos_tags_plural." + tag ), value: tag}
})
items.unshift({text: this.$t('all_pos'), value: null})
return items
}
},
methods: {
reset () {
this.$emit('updatePos', "ALL")
this.$emit('updateScope', "ei")
},
updatePos (pos) {
this.$emit('updatePos', pos)
}
}
}
</script>
<style scoped>
.v-card {
padding-top: 0px !important;
padding-bottom: 24px !important;
padding-top: 10px;
margin-top: 10px;
min-height: 36px;
}
.v-list-item {
padding-left: 24px;
padding-right: 24px;
}
.search_options {
position: absolute;
min-height: 36px;
bottom: 0px;
left: 0px;
border-radius: 28px;
float: left;
width: 100%;
}
.search_options.expanded {
border-top-left-radius: 0px;
border-top-right-radius: 0px;
min-height: 24px;
}
#desktop_toolbar {
display: flex;
padding-left: 10px;
margin-right: 10px;
gap: 24px;
}
#search_toolbar {
margin-bottom: 10px;
}
.md#search_toolbar, .lg#search_toolbar, .xl#search_toolbar {
padding-top: 10px;
}
#desktop_toolbar v-combobox {
width: 300px;
}
#toolbar-pos-select {
max-width: 200px;
}
.v-input {
margin-top: 0px;
}
</style>
<template>
<div>
<v-list subheader>
<v-list-item>
<v-checkbox
v-model="toggleInflectionExpanded"
:label="$t('settings.inflection_expanded')"
hide-details>
</v-checkbox>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="toggleInflectionNo"
:label="$t('settings.inflection_no')"
hide-details>
</v-checkbox>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="disableHotkey"
:label="$t('settings.disable_hotkey')"
hide-details>
</v-checkbox>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="toggleInflectionTableContext"
:label="$t('settings.inflection_table_context')"
hide-details>
</v-checkbox>
</v-list-item>
<v-list-item>
<v-radio-group mandatory two-line v-model="updateCollapse" :label="$t('settings.collapse.description')">
<v-radio value="auto" :label="$t('settings.collapse.auto')"></v-radio>
<v-radio value="always" :label="$t('settings.collapse.always')"></v-radio>
<v-radio value="never" :label="$t('settings.collapse.never')"></v-radio>
</v-radio-group>
</v-list-item>
</v-list>
</div>
</template>
<script>
export default {
name: "Settings",
computed: {
toggleInflectionNo: {
get () { return this.$store.state.showInflectionNo},
set () { this.$store.commit("toggleInflectionNo")
}
},
disableHotkey: {
get () { return this.$store.state.disableHotkey},
set () { this.$store.commit("disableHotkey")
}
},
toggleInflectionExpanded: {
get () { return this.$store.state.inflectionExpanded},
set () { this.$store.commit("toggleInflectionExpanded")
}
},
toggleInflectionTableContext: {
get () {
return this.$store.state.inflectionTableContext},
set () {
this.$store.commit("toggleInflectionTableContext")
}
},
updateCollapse: {
get () { return this.$store.state.collapseArticles},
set(value) {
this.$store.commit("setCollapse", value)
}
},
},
}
</script>
<template>
<div :lang="$parent.lang_tag_locale" class = "split-inf" v-if="split_inf">kløyvd infinitiv: <em>-a</em>
<v-menu tile
v-model="menu"
offset-x max-width="200px">
<template v-slot:activator="{ on, attrs }">
<v-btn :aria-label="$t('split_inf.label', $parent.content_locale)"
small
icon
v-on="on"
v-bind="attrs"
class="info-button">
<v-icon color="primary"
small>info</v-icon>
</v-btn>
</template>
<v-card tile
class="info-card">
{{$t('split_inf.content[0]', $parent.content_locale)}} <em>-a</em> {{$t('split_inf.content[1]', $parent.content_locale)}}
<a target="_blank"
href="https://www.sprakradet.no/svardatabase/sporsmal-og-svar/kloyvd-infinitiv-/">{{$t('split_inf.content[2]', $parent.content_locale)}}</a>
</v-card>
</v-menu>
</div>
</template>
<script>
export default {
name: "SplitInf",
props: {
lemmas: Array
},
data: function() {
return {
menu: false,
long_lemma: false
}
},
computed: {
split_inf: function() {
return this.lemmas[0].split_inf
}
}
}
</script>
<style>
.split-inf {
padding-top: 10px;
}
</style>
\ No newline at end of file
......@@ -4,8 +4,8 @@
{{body.lemmas[0]}}
</span>
<ul>
<DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" @article-click="article_link_click" />
<Definition :level="9" :body="body.article.body.definitions[0]" :dictionary="dictionary" @article-click="article_link_click" />
<DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" @article-click="article_link_click" @error="article_error"/>
<Definition :level="9" :body="body.article.body.definitions[0]" :dictionary="dictionary" @article-click="article_link_click" @error="article_error"/>
</ul>
</li>
</template>
......@@ -33,6 +33,9 @@ export default {
methods: {
article_link_click: function(item) {
this.$emit('article-click', item)
},
article_error: function(payload) {
this.$emit('error', payload)
}
}
}
......
<template>
<div>
<v-app-bar class = "top-bar" dense elevation="0" dark color="primary">
<v-app-bar-title class="beta" :title="release" aria-label="Ordbøkene: Bokmålsordboka og Nynorskordboka"><a href ="/">{{$vuetify.breakpoint.smAndDown ? 'Ordbøkene' : 'ordbøkene.no'}}</a></v-app-bar-title>
<v-spacer/>
<v-divider vertical/>
<v-toolbar-items>
<v-divider vertical />
<v-btn id="locale_button"
aria-haspopup="dialog"
aria-label="Locale | Visningsspråk"
icon @click.native="chooseLocale = !chooseLocale">
<v-icon>language</v-icon></v-btn>
<v-divider vertical />
<v-btn id="top_menu"
:aria-expanded="menuOpen? 'true' : 'false'"
aria-haspopup="dialog"
width="96px"
aria-labelledby="menu_label"
text @click.native="menuOpen = !menuOpen">
<span id="menu_label">{{$t('menu.title')}}</span><v-icon right>{{menuOpen? 'close' : 'menu'}}</v-icon></v-btn>
</v-toolbar-items>
<v-divider vertical/>
</v-app-bar>
<v-dialog origin="48px center" content-class="dialogMenu" aria-labelledby="menu_label" max-width="1200" width="1200" v-model="menuOpen" hide-overlay
:close-on-content-click="false" transition="dialog-top-transition">
<Menu @close="menuOpen=false"/>
</v-dialog>
<v-dialog max-width="600px" v-model="chooseLocale">
<v-card>
<v-toolbar elevation="0" color="primary" text>
<v-toolbar-title class="language-dialog-title">Locale | Visningsspråk</v-toolbar-title></v-toolbar>
<v-card-text class="text--primary">
<v-list three-line>
<v-list-item link v-on:click="choose_locale('nob')">
<v-list-item-content>
<v-list-item-title>Norsk (bokmål)</v-list-item-title>
<v-list-item-subtitle>Vis overskrifter, knapper og menyer på bokmål</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item link v-on:click="choose_locale('nno')">
<v-list-item-content>
<v-list-item-title>Norsk (nynorsk)</v-list-item-title>
<v-list-item-subtitle>Vis overskrifter, knappar og menyar på nynorsk</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item link v-on:click="choose_locale('eng')">
<v-list-item-content>
<v-list-item-title>English</v-list-item-title>
<v-list-item-subtitle>Choose English as display language for headings, buttons and menus</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item link v-on:click="choose_locale('ukr')">
<v-list-item-content>
<v-list-item-title>Українська (ukrainsk)</v-list-item-title>
<v-list-item-subtitle>Виберіть українську як мову відображення заголовків, кнопок і меню</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import Menu from './Menu.vue'
export default {
name: "TopBar",
data: function() {
return {
version_label: process.env.VUE_APP_VERSION_LABEL,
release: process.env.VUE_APP_RELEASE,
chooseLocale: false
}
},
computed: {
menuOpen: {
get() {
return this.$store.state.menuOpen
},
set () {
this.$store.commit('toggleMenu')
}
}
},
methods: {
home: function() {
this.$router.push('/')
},
choose_locale: function(locale) {
this.$store.commit("setLocale", {value: locale, i18n: this._i18n})
this.chooseLocale = false
}
},
components: {
Menu
},
}
</script>
<style >
.dialogMenu {
margin: 0px !important;
align-self: flex-start !important;
margin-top: 48px !important;
}
.v-app-bar-title a {
text-decoration: none;
color: white !important;
}
</style>
\ No newline at end of file
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
function loadLocaleMessages () {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
}
})
return messages
}
export default new VueI18n({
fallbackLocale: 'eng',
silentTranslationWarn: true,
messages: loadLocaleMessages()
})
{
"sub_title": "Bokmålsordboka and Nynorskordboka",
"monthly": "Titbits from the dictionaries",
"pick_ukrainian": "try it",
"pick_ukrainian_long": "try ukrainian display language",
"ukrainian_flag": "Ukrainian flag",
"photo": "Photo by Alina Scheck (unsplash.com)",
"close": "Close",
"cancel": "Cancel",
"or": " or ",
"and": " and ",
"from": "from",
"dicts_from": {
"bm": "bokmål dictionary",
"nn": "nynorsk dictionary"
},
"to_top": "Back to top",
"home": "Home",
"accessibility_statement": "Accessibility statement",
"per_page": "Results per page: ",
"search_placeholder": "Search in ",
"footer_description": " show spelling and inflection in line with official Norwegian spelling. The Language Council and the University of Bergen are behind the dictionaries.",
"accessibility": {
"menu": "Open top menu",
"main_content": "Skip to main content",
"bm": "Skip to results in the Bokmål dictionary",
"nn": "Skip to results in the Nynorsk dictionary",
"homograph": "Meaning "
},
"dicts": {
"nn": "Nynorsk dictionary",
"bm": "Bokmål dictionary",
"bm,nn": "Both dictionaries"
},
"dicts_inline": {
"nn": "the Nynorsk dictionary",
"bm": "the Bokmål dictionary",
"bm,nn": "the dictionaries"
},
"dicts_short": {
"nn": "nynorsk",
"bm": "bokmål",
"bm,nn": "both"
},
"menu": {
"title": "Menu",
"help": "Help",
"about": "About"
},
"options": {
"title": "Search options",
"inflected": "Include inflected forms",
"fulltext": "Full-text search",
"pos": "Part of speech",
"collapse": "Collapse articles"
},
"all_pos": "all parts of speech",
"tags": {
"NOUN": "noun",
"VERB": "verb",
"ADJ": "adjective",
"ADP": "preposition",
"PFX": "prefix",
"ADV": "adverb",
"DET": "determinative",
"PROPN": "proper noun",
"ABBR": "abbreviation",
"INTJ": "interjection",
"SYM": "symbol",
"PRON": "pronoun",
"CCONJ": "conjunction",
"SCONJ": "subjunction",
"INFM": "infinitive particle",
"COMPPFX": "in compounds",
"Masc": "masculine",
"Fem": "feminine",
"Neuter": "neuter",
"Uninfl": "uninflectable"
},
"pos_tags_plural": {
"NOUN": "nouns",
"PRON": "pronouns",
"DET": "determinatives",
"ADJ": "adjectives",
"CCONJ": "conjunctions",
"SCONJ": "subjunctions",
"ADV": "adverbs",
"ADP": "prepositions",
"VERB": "verbs",
"INTJ": "interjections"
},
"determiner": {
"Quant": "quantifier",
"Dem": "demonstrative",
"Poss": "possessive"
},
"split_inf": {
"title": "split infinitive",
"label": "More information about split infinitive",
"content": ["If you use split infinitive, the infinitive form of this verb will have the suffix", "", "Read more here (Norwegian)"]
},
"settings": {
"title": "Settings",
"inflection_no": "Show inflection codes",
"disable_hotkey": "Disable search field hotkey",
"inflection_expanded": "Always show inflection tables in expanded articles",
"inflection_table_context": "Show context for participle forms in the inflection tables",
"locale": {
"title": "Locale" ,
"description": "Change the language of headings, buttons, messages, and menus. This will not affect examples and definitions: the Bokmål dictionary will remain in Bokmål and the Nynorsk dictionary stays Nynorsk. "
},
"dict": {
"title": "Default dictionaries",
"description": "Default dictionary settings for which of the dictionaries to be queried when using ordbokene.no. The settings will not affect current searches or links to searches in dictionaries. To change the dictionary during use, change the settings temporarily via the menu to the right of the search field. "
},
"collapse": {
"description": "Default search presentation settings. Choose between seeing complete articles, article excepts or an automatically generated presentation.",
"auto": "Auto (excerpts when more than two results)",
"always": "Show excerpts of articles",
"never": "Show complete articles"
},
"reset": "Delete stored settings"
},
"contact": {
"title": "Contact us",
"content": [
"The content of the dictionaries",
"Functionality and bug reports",
"Feedback and questions regarding words, inflected forms and other content of the dictionaries: ",
"Report bugs or give feedback regarding the user interface: "
],
"faq": {
"title": "Frequently asked questions:",
"items": [
{"title": "Why are some words missing in the dictionaries?",
"text": [" are not to be regarded as lists of words permitted to use in Norwegian. There are many words – old as well as new – that are not in the dictionaries. But you can use anyway. New words do not need official approval before they can be used. It is the language users who jointly decide which words are commonly used in Norwegian. Normally, new words do not enter the dictionaries until they have been in use for some time and are well-established in the language. The dictionary editors decide which words are to be included based on surveys of large text corpora, and the Norwegian Language Council decides how the words are to be spelled and inflected. ",
"Read more in the About-page"]
},
{"title": "Where are the inflection codes from the printed dictionaries?", "text": "You can toggle this feature in settings, which is located at the bottom of the page and in the navigation menu."},
{"title": "Can I avoid having to click “show inflection” and “show article”?", "text": "Yes, you can change this in settings, which is located at the bottom of the page and in the navigation menu. Choose “Always show inflection tables in expanded articles” and “Show complete articles” if you want to reduce the number of clicks as much as possible."}
]
}
},
"notifications": {
"inflected": " is a form of ",
"similar": "Did you mean:",
"similar_bm": "Add similar results from Bokmålsordboka: ",
"similar_nn": "Add similar results from the Nynorsk dictionary: ",
"search": "Search",
"back": "Back to search",
"no_results": "No results",
"results": "results",
"ignored_words": "The search was ignored because we limit queries to 20 words.",
"ignored_chars": "The search was ignored because we do not accept words with more than 40 characters.",
"no_pos_results": " (no matching {pos})",
"suggest_dict": [
"Did you search in the wrong dictionary?",
"Try searching"
],
"fulltext": "Suggested full-text searches:"
},
"article": {
"definition": "Definition",
"show": "Show article",
"show_inflection": "Inflection",
"hide_inflection": "Inflection",
"share": "Share the word",
"copy_link": "Copy link",
"cite": "Cite",
"cite_title": "Cite the article",
"cite_description": ["If you want to cite this article in the ", " we recommend that you state when the article was retrieved (read), e. g. as follows:"],
"copy": "Copy",
"download": "Download RIS file",
"link_copied": "Link copied to the clipboard",
"citation_copied": "Citation copied to the clipboard",
"citation": "« {lemma} ». In: <em> {dict}. </em> Språkrådet & Universitetet i Bergen. &lt;<a href='{link}'> {link} </a>&gt; ( retrieved {dd}. {mm}. {yyyy}). ",
"headings": {
"etymology": "Etymology",
"pronunciation": "Pronunciation",
"expressions": "Set phrases",
"definitions": "Senses and Example Sentences",
"examples": "Example"
}
},
"error": {
"article": "An error has occured in article {no} in {dict}",
"404": {
"title": "404: Not Found",
"description": "The page may have been deleted or moved, or you may have used an invalid link."
},
"503": {
"title": "503: Unavailable service",
"description": "The server is temporarily overloaded or under maintenance."
},
"server": {
"title": "Server Error",
"description": "Something went wrong on the server side (status code: {code})"},
"generic_code": {
"title": "Error",
"description": "An unexpected error occurred while communicating with the server (status code: {code})"},
"network": {
"title": "Connection Error",
"description": "A network problem prevented the page from loading. Please try reloading the page or check if you are connected to the internet."},
"no_article": "The server could not find an article with ID {id}. The article may have been deleted or moved, or you may have used an invalid link.",
"generic": {
"title": "Error",
"description": "Something went wrong ..."
}
}
}
\ No newline at end of file
{
"sub_title": "Bokmålsordboka og Nynorskordboka",
"photo": "Foto: Alina Scheck (unsplash.com)",
"background": "Bakgrunnsbilde",
"monthly": "Smakebitar frå ordbøkene",
"pick_ukrainian": "prøv det",
"pick_ukrainian_long": "prøv ukrainske hjelpetekster",
"ukrainian_flag": "Ukrainsk flagg",
"close": "Lukk",
"cancel": "Avbryt",
"to_top": "Til toppen",
"home": "Til forsida",
"accessibility_statement": "Tilgjengeerklæring",
"per_page": "Resultat per side: ",
"or": " eller ",
"and": " og ",
"from": "frå",
"dicts_from": {
"bm": "bokmålsordboka",
"nn": "nynorskordboka"
},
"search_placeholder": "Søk i ",
"footer_description": " viser skrivemåte og bøying i tråd med norsk rettskriving. Språkrådet og Universitetet i Bergen står bak ordbøkene.",
"accessibility": {
"menu": "Opne toppmeny",
"main_content": "Gå til hovudinnhald",
"bm": "Gå til treff i Bokmålsordboka",
"nn": "Gå til treff i Nynorskordboka",
"homograph": "Tyding "
},
"dicts": {
"nn": "Nynorskordboka",
"bm": "Bokmålsordboka",
"bm,nn": "begge ordbøkene"
},
"dicts_inline": {
"nn": "Nynorskordboka",
"bm": "Bokmålsordboka",
"bm,nn": "ordbøkene"
},
"dicts_short": {
"nn": "nynorsk",
"bm": "bokmål",
"bm,nn": "begge"
},
"menu": {
"title": "Meny",
"help": "Hjelp til søk",
"about": "Om ordbøkene"
},
"options": {
"title": "Søkjealternativ",
"inflected": "Søk i bøygde former",
"fulltext": "Fritekstsøk",
"pos": "Ordklasse",
"collapse": "Komprimer artiklar"
},
"all_pos": "alle ordklasser",
"tags": {
"NOUN": "substantiv",
"VERB": "verb",
"ADJ": "adjektiv",
"ADP": "preposisjon",
"PFX": "prefiks",
"ADV": "adverb",
"DET": "determinativ",
"PROPN": "eigennamn",
"ABBR": "forkorting",
"INTJ": "interjeksjon",
"SYM": "symbol",
"PRON": "pronomen",
"CCONJ": "konjunksjon",
"SCONJ": "subjunksjon",
"INFM": "infinitivsmerke",
"COMPPFX": "i samansetjing",
"Masc": "hankjønn",
"Fem": "hokjønn",
"Neuter": "inkjekjønn",
"Uninfl": "ubøyeleg"
},
"pos_tags_plural": {
"NOUN": "substantiv",
"PRON": "pronomen",
"DET": "determinativ",
"ADJ": "adjektiv",
"CCONJ": "konjunksjonar",
"SCONJ": "subjunksjonar",
"ADV": "adverb",
"ADP": "preposisjonar",
"VERB": "verb",
"INTJ": "interjeksjonar"
},
"determiner": {
"Quant": "kvantor",
"Dem": "demonstrativ",
"Poss": "possessiv"
},
"split_inf": {
"title": "kløyvd infinitiv",
"label": "Meir informasjon om kløyvd infinitiv",
"content": ["Dersom du bruker kløyvd infinitiv, skal dette verbet ha", "i infinitiv.", "Les meir her"]
},
"settings": {
"title": "Innstillingar",
"inflection_no": "Vis bøyingskodane frå dei trykte ordbøkene",
"disable_hotkey": "Slå av hurtigtast for søkjefeltet",
"inflection_expanded": "Vis alltid bøyingstabellane i fullstendige artiklar",
"inflection_table_context": "Vis kontekst for partisippformer i bøyingstabellane",
"locale": {
"title": "Språk",
"description": "Her kan du endre språk på knappar, meldingar og menyar. Dette påverkar ikkje tydingar og døme i ordbøkene: Bokmålsordboka kjem framleis til å vere på bokmål, og Nynorskordboka kjem til å vere på nynorsk."
},
"collapse": {
"description": "Vel når artiklane skal komprimerast til eit utdrag av tydingane.",
"auto": "Automatisk (meir enn to resultat)",
"always": "Vis berre utdrag",
"never": "Vis berre fullstendige artiklar"
},
"reset": "Slett lagra innstillingar"
},
"contact": {
"title": "Kontakt oss",
"content": [
"Innhaldet i ordbøkene",
"Funksjonalitet og tekniske feil",
"Spørsmål og tilbakemeldingar med omsyn til ord, bøyingsformer og anna innhald i ordbøkene: ",
"Rapporter tekniske feil og gje tilbakemeldingar på brukargrensesnittet: "
],
"faq": {
"title": "Ofte stilte spørsmål:",
"items": [
{"title": "Kvifor manglar somme ord i ordbøkene?",
"text": [" er ikkje å rekne som lister over kva ord som er tillatne å bruke på norsk. Det finst mange ord, både gamle og nye, som ikkje er med i ordbøkene, men som du gjerne kan bruke likevel. Det er heller ikkje slik at nye ord må godkjennast før dei kan takast i bruk, det er språkbrukarane som i fellesskap avgjer kva ord som er gangbare i norsk. Normalt kjem ikkje nye ord inn i ordbøkene før dei har vore i bruk ei stund og er etablerte i språket. Ordbokredaksjonen avgjer kva ord som skal vere med basert på undersøkingar av store tekstsamlingar, og Språkrådet avgjer korleis orda skal stavast og bøyast. ",
"Les meir på informasjonssida"]
},
{"title": "Kvar er bøyingskodane frå dei trykte ordbøkene?", "text": "Du kan hake av for «Vis bøyingskodane frå dei trykte ordbøkene» i innstillingane, som du finn nedst på sida og i menyen øvst til høgre på sida."},
{"title": "Kan eg sleppe å trykkje «vis bøying» og «vis artikkel»?", "text": "Ja, du kan endre dette i innstillingane, som du finn nedst på sida og i menyen øvst til høgre på sida. Hak av for «Vis alltid bøyingstabellane i fullstendige artiklar» og «Vis berre fullstendige artiklar» dersom du ønskjer minst mogleg klikking."}
]
}
},
"notifications": {
"inflected": " er ei form av ",
"similar": "Meinte du:",
"similar_bm": "Legg til liknande treff frå Bokmålsordboka: ",
"similar_nn": "Legg til liknande treff frå Nynorskordboka: ",
"search": "Søk i ordbøkene",
"back": "Tilbake til søkjeresultata",
"no_results": "Ingen treff",
"results": "treff",
"ignored_words": "Søket vart ignorert fordi vi avgrensar talet på førespurnader til 20 ord.",
"ignored_chars": "Søket vart ignorert fordi vi ikkje tillèt søkjeord med meir enn 40 teikn.",
"no_pos_results": " i søk på {pos}",
"suggest_dict": [
"Har du søkt i feil ordbok?",
"Prøv å søkje i "
],
"fulltext": "Forslag til fritekstsøk:"
},
"article": {
"definition": "Definisjon",
"show": "Vis artikkel",
"show_inflection": "Vis bøying",
"hide_inflection": "Skjul bøying",
"share": "Del ordet",
"copy_link": "Kopier lenkje",
"cite": "Sitere",
"cite_title": "Sitere artikkelen",
"cite_description": ["Ønskjer du å sitere denne artikkelen i ", ", rår vi deg til å gje opp når artikkelen vart henta (lesen), t.d. slik:"],
"copy": "Kopier",
"download": "Last ned RIS-fil",
"link_copied": "Lenkja er kopiert til utklippstavla",
"citation_copied": "Sitatet er kopiert til utklippstavla",
"citation": "«{lemma}». I: <em>{dict}.</em> Språkrådet og Universitetet i Bergen. &lt;<a href='{link}'>{link}</a>&gt; (lesen {dd}.{mm}.{yyyy}).",
"headings": {
"etymology": "Opphav",
"pronunciation": "Uttale",
"expressions": "Faste uttrykk",
"definitions": "Tyding og bruk",
"examples": "Døme"
}
},
"error": {
"article": "Det har oppstått ein feil i artikkel {no} i {dict}",
"404": {
"title": "404: Ikkje funne",
"description": "Sida kan ha blitt sletta eller flytta, eller du kan ha brukt ei ugyldig lenkje."
},
"503": {
"title": "503: Utilgjengeleg teneste",
"description": "Tenaren er mellombels overbelasta eller under vedlikehald."
},
"server": {
"title": "Feil på server",
"description": "Noko gjekk gale på serversida (statuskode: {code})"},
"generic_code": {
"title": "Feil",
"description": "Det har oppstått ein uventa feil i kommunikasjonen med serveren (statuskode: {code})"},
"network": {
"title":"Tilkoplingsfeil",
"description": "Eit nettverksproblem hindra lasting av sida. Prøv å laste sida på nytt, eller sjekk om du er kopla til internett."},
"no_article": "Serveren finn ingen artikkel med ID {id}. Artikkelen kan ha blitt sletta eller flytta, eller du kan ha brukt ei ugyldig lenkje.",
"generic": {
"title": "Feil",
"description":"Noko gjekk gale..."
}
}
}