Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ yarn build
yarn lint
```

### Internationalization

This project uses `vue-i18n` for internationalization. All locale files are located in the `src/locales` directory.

**Adding or Updating Translations**

To add or update a translation for an existing language, edit the corresponding JSON file in `src/locales`. For example, to update a French translation, you would edit `src/locales/fr.json`.

**Adding a New Language**

1. Create a new JSON file in `src/locales` with the two-letter locale code for the new language (e.g., `es.json` for Spanish).
2. Copy the contents of `src/locales/en.json` into your new file.
3. Translate all the string values into the new language.
4. Import the new locale file in `src/main.js` and add it to the `messages` object in the `createI18n` configuration.
5. Add the new language to the `languageNames` object in `src/App.vue` to make it available in the language switcher.

### Recommended Browser Setup

- Chromium-based browsers (Chrome, Edge, Brave, etc.):
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dotenv": "^17.2.3",
"moment": "^2.30.1",
"vue": "^3.5.22",
"vue-i18n": "^11.2.1",
"vue-router": "^4.6.3",
"vue3-flip-countdown": "^0.1.6",
"vue3-markdown-it": "^1.0.10"
Expand All @@ -27,6 +28,7 @@
"eslint-plugin-vue": "~10.5.0",
"globals": "^16.4.0",
"vite": "^7.1.11",
"vite-plugin-vue-devtools": "^8.0.3"
"vite-plugin-vue-devtools": "^8.0.3",
"vue-eslint-parser": "^10.2.0"
}
}
43 changes: 37 additions & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
<template>
<div id="app" :class="darkClass">
<div class="language-switcher">
<select v-model="locale">
<option
v-for="loc in availableLocales"
:key="`locale-${loc}`"
:value="loc"
>
{{ languageNames[loc] }}
</option>
</select>
</div>
<Challenges
@closeToolbar="toggleOptions"
@previewOff="previewOff"
Expand All @@ -15,22 +26,32 @@
<a href="https://dribdat.cc" target="_blank">dribdat</a>
&#x1F3C0;
<a v-if="allowToolbar" @click="toggleOptions" class="options"
><span>options</span></a
><span>{{ $t('options') }}</span></a
>
</div>
</div>
</template>

<script>
import { ref, onMounted } from "vue"
import Challenges from "./components/Challenges.vue"
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import Challenges from "./components/Challenges.vue";

export default {
name: "App",
components: {
Challenges,
},
setup() {
const { locale, availableLocales } = useI18n();
const languageNames = {
en: "English",
fr: "Français",
de: "Deutsch",
it: "Italiano",
uk: "Українська",
hi: "हिन्दी",
};
const dribdatApi = ref(null);
const dribdatHome = ref("#top");
const dribdatDribs = ref("");
Expand Down Expand Up @@ -115,12 +136,22 @@ export default {
previewOff,
previewOn,
setDarkMode,
}
}
}
locale,
availableLocales,
languageNames,
};
},
};
</script>

<style>
.language-switcher {
position: absolute;
top: 10px;
right: 10px;
z-index: 1001;
}

@import 'https://cdn.jsdelivr.net/gh/maxwell-k/dejavu-sans-mono-web-font/index.css';

#app {
Expand Down
72 changes: 38 additions & 34 deletions src/components/Challenges.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="challenges">
<Header v-if="isHeadline" :event="event"></Header>
<EventHeader v-if="isHeadline" :event="event"></EventHeader>

<div class="grid-container" v-if="!isHexagons">
<div
Expand Down Expand Up @@ -67,18 +67,18 @@
</div>

<div class="team-join" v-if="isButtons">
<button @click="joinTeam(project)" title="Join">🏀</button>
<button @click="joinTeam(project)" :title="$t('join')">🏀</button>
<button
v-if="isComments"
@click="openComment(project)"
title="Comment"
:title="$t('comment')"
>
💬
</button>
<button
v-show="project.contact_url"
@click="contactTeam(project)"
title="Contact"
:title="$t('contact')"
>
👋
</button>
Expand Down Expand Up @@ -109,9 +109,9 @@

<Countdown v-if="isCountdown" :event="event"></Countdown>

<Footer v-if="isHeadline" :event="event"></Footer>
<EventFooter v-if="isHeadline" :event="event"></EventFooter>

<div class="loading" v-if="projects == null" title="Loading ...">
<div class="loading" v-if="projects == null" :title="$t('loading')">
<i class="ball">🏀</i>
</div>

Expand All @@ -121,30 +121,31 @@
<button
class="modal-close-button"
@click="$emit('closeToolbar')"
title="Close"
:title="$t('close')"
>
</button>
<span class="share-button btn">
🌐<a :href="shareUrl()">Share</a>
🌐<a :href="shareUrl()">{{ $t('share') }}</a>
</span>
&nbsp;
<input type="checkbox" v-model="isHeadline" id="isHeadline" />
<label for="isHeadline" title="Header">⛳</label>
<label for="isHeadline" :title="$t('header')">⛳</label>
<input type="checkbox" v-model="isChallenges" id="isChallenges" />
<label for="isChallenges" title="Show Challenges">🏆</label>
<label for="isChallenges" :title="$t('show_challenges')">🏆</label>
<input type="checkbox" v-model="isHexagons" id="isHexagons" />
<label for="isHexagons" title="Hexgrid mode">⬣</label>
<label for="isHexagons" :title="$t('hexgrid_mode')">⬣</label>
<input type="checkbox" v-model="isCountdown" id="isCountdown" />
<label for="isCountdown" title="Countdown">⏰</label>
<label for="isCountdown" :title="$t('countdown')">⏰</label>
<input type="checkbox" v-model="isPreviews" id="isPreviews" />
<label for="isPreviews" title="Pop-ups">👀</label>
<label for="isPreviews" :title="$t('pop_ups')">👀</label>
<input type="checkbox" v-model="isExcerpts" id="isExcerpts" />
<label for="isExcerpts" title="Excerpts">🖼️</label>
<label for="isExcerpts" :title="$t('excerpts')">🖼️</label>
<input type="checkbox" v-model="isButtons" id="isButtons" />
<label for="isButtons" title="Join/Contact button">🪟</label>
<label for="isButtons" :title="$t('join_contact_button')">🪟</label>
<input type="checkbox" v-model="isComments" id="isComments" />
<label for="isComments" title="Comment buttons">💬</label>
<label for="isComments" :title="$t('comment_buttons')">💬</label>
<label for="darkMode">{{ $t('dark_mode') }}</label>
<select v-model="darkMode" id="darkMode" @change="changeDark">
<option value="default" selected>🌗</option>
<option
Expand All @@ -156,6 +157,7 @@
</option>
</select>
&nbsp;
<label for="sortBy">{{ $t('sort_by') }}</label>
<select v-model="sortOrder" id="sortBy" @change="changeOrder">
<option value="default" selected>📚</option>
<option
Expand All @@ -173,9 +175,10 @@

<script>
import { ref, onMounted, computed } from "vue";
import { useI18n } from "vue-i18n";
import moment from "moment";
import Header from "./Header.vue";
import Footer from "./Footer.vue";
import EventHeader from "./Header.vue";
import EventFooter from "./Footer.vue";
import Previews from "./Previews.vue";
import Countdown from "./Countdown.vue";
import ProjectHoneycomb from "./Honeycomb.vue";
Expand All @@ -192,10 +195,11 @@ export default {
Countdown,
ProjectHoneycomb,
Previews,
Header,
Footer,
EventHeader,
EventFooter,
},
setup(props, { emit }) {
const { t } = useI18n();
const event = ref({});
const projects = ref(null);
const activities = ref(null);
Expand All @@ -211,19 +215,19 @@ export default {
const isExcerpts = ref(false);
const activePreview = ref(-1);
const sortOrder = ref("title");
const sortOptions = ref([
{ id: "id", name: "id" },
{ id: "ident", name: "Ident" },
{ id: "name", name: "Name" },
{ id: "summary", name: "Summary" },
{ id: "hashtag", name: "Hashtag" },
{ id: "score", name: "Score" },
const sortOptions = computed(() => [
{ id: "id", name: t("id") },
{ id: "ident", name: t("ident") },
{ id: "name", name: t("name") },
{ id: "summary", name: t("summary") },
{ id: "hashtag", name: t("hashtag") },
{ id: "score", name: t("score") },
]);
const darkMode = ref("default");
const darkOptions = ref([
{ id: "default", name: "System" },
{ id: "light", name: "Light" },
{ id: "dark", name: "Dark" },
const darkOptions = computed(() => [
{ id: "default", name: t("system") },
{ id: "light", name: t("light") },
{ id: "dark", name: t("dark") },
]);

const filterProjects = computed(() => {
Expand Down Expand Up @@ -334,7 +338,7 @@ export default {
}
}
if (!datasrc) {
errorMessage.value = "No data source provided.";
errorMessage.value = t("no_data_source");
return;
}
let dribs_url = props.dribs;
Expand Down Expand Up @@ -370,7 +374,7 @@ export default {
}
});
if (data.projects === null) {
return Promise.reject("Project data not found");
return Promise.reject(t("project_data_not_found"));
}
}
projects.value = [];
Expand Down Expand Up @@ -447,7 +451,7 @@ export default {
})
.catch((err) => {
if (err.message.indexOf('not valid JSON')) {
errorMessage.value = 'Could not load valid hackathon metadata.'
errorMessage.value = t("could_not_load_metadata");
console.warn(err.message);
console.log(datasrc);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Countdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
required: true,
}
},
setup(props) {
setup() {
let timespan = ref("");
let deadline = ref("2000-01-01 12:00");
return {
Expand Down
6 changes: 4 additions & 2 deletions src/components/ModalFrame.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<template>
<section>
<button @click="isVoteActive = true">🗳️ Contribute</button>
<button @click="isVoteActive = true">🗳️ {{ $t('contribute') }}</button>

<Modal v-if="isVoteActive" @close="isVoteActive = false">
<div class="content" slot="body">
<template v-slot:body>
<div class="content" >
<iframe
:src="framesrc"
width="100%"
Expand All @@ -14,6 +15,7 @@
>Loading…</iframe
>
</div>
</template>
</Modal>
</section>
</template>
Expand Down
Loading