Skip to content

Commit

Permalink
[TECH-2925] Refactor component translations (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
av-alexistoledo authored Jan 18, 2024
2 parents c23119c + 745d286 commit 7563a49
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 200 deletions.
8 changes: 4 additions & 4 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
},
"dependencies": {
"@aion-dk/js-client": "^3.1.19",
"@assemblyvoting/ui-library": "^3.0.10",
"@assemblyvoting/ui-library": "^3.0.15",
"@kalimahapps/vue-popper": "^1.1.6",
"@pinia/testing": "^0.0.15",
"@playwright/test": "^1.32.3",
Expand Down
10 changes: 5 additions & 5 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Footer from "./components/Footer.vue";
import router from "./router";
import { loadLocaleMessages, setLocale } from "./lib/i18n";
import i18n from "./lib/i18n";
import type { Locale } from "./Types";
import type { Locale } from "vue-i18n";
import { fallbackMessages } from "./assets/translations";
import { defaultTheme } from "./assets/theme";
Expand Down Expand Up @@ -68,11 +68,11 @@ const setConfigurations = async (
};
const setLanguage = async (conferenceClient: any) => {
let browserLocale = navigator.languages.find((locale) =>
i18n.global.availableLocales.includes(locale as Locale)
let browserLocale: Locale = navigator.languages.find((locale: Locale) =>
i18n.global.availableLocales.includes(locale)
);
if (browserLocale) setLocale(browserLocale as Locale);
if (browserLocale) setLocale(browserLocale);
let paramLocale = router.currentRoute.value.params.locale?.toString();
Expand All @@ -97,7 +97,7 @@ const setLanguage = async (conferenceClient: any) => {
response
? loadLocaleMessages(locale, response)
: loadLocaleMessages(locale, (fallbackMessages as any)[locale]);
: loadLocaleMessages(locale, fallbackMessages[locale]);
}
}
};
Expand Down
13 changes: 11 additions & 2 deletions client/src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
MarkingType,
OptionContent,
} from "@aion-dk/js-client/dist/lib/av_client/types";
import type { DefineLocaleMessage, Locale } from "vue-i18n";

export type Election = any;

Expand Down Expand Up @@ -79,8 +80,6 @@ export interface Theme {
splash: string | null;
}

export type Locale = "en" | "da";

export interface FullOptionContent extends OptionContent {
url?: LocalString;
videoUrl?: LocalString;
Expand All @@ -99,3 +98,13 @@ export interface FullContestContent extends ContestContent {
markingType: FullMarkingType;
votesAllowedPerOption?: number;
}

export interface CurrentTranslations {
translations: {
[locale: Locale]: {
js: DefineLocaleMessage;
};
};
}

export type SpreadableDLM = { [key: string]: any };
4 changes: 3 additions & 1 deletion client/src/assets/translations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const fallbackMessages = {
import type { SpreadableDLM } from "../Types";

export const fallbackMessages: SpreadableDLM = {
en: {
locales: {
en: "English",
Expand Down
193 changes: 15 additions & 178 deletions client/src/components/BallotVerifierContest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,26 @@
</h3>
<p v-if="contest.question" v-text="contest.question[$i18n.locale]" />
<div
v-for="(pile, pIndex) in contestSelection.piles"
class="BallotVerifierContest__Pile"
:key="pIndex"
v-for="(selectionPile, pileIndex) in contestSelection.piles"
:key="JSON.stringify(selectionPile)"
class="BallotVerifierContest__Piles"
>
<div class="BallotVerifierContest__Header">
<span>
{{
`${$t("views.verifier.spoiled.ballot_selection")} ${pIndex + 1}/${
contestSelection.piles.length
}`
}}
</span>
<strong>
<span>{{
`${$t("views.verifier.spoiled.assigned")} ${pile.multiplier}`
}}</span>
</strong>
</div>
<div class="BallotVerifierContest__Options">
<div
v-if="pile.optionSelections.length === 0"
class="BallotVerifierContest__Option"
>
<div class="BallotVerifierContest__Option_Title">
{{ $t("views.verifier.blank_pile") }}
</div>
<AVOptionCheckbox
:checked="true"
:disabled="true"
class="BallotVerifierContest__Option_Cross"
/>
</div>
<div
class="BallotVerifierContest__Option"
v-else
v-for="(parsedOption, oIndex) in parseOptions(pile)"
:key="`pile-${pIndex}-option-${oIndex}`"
>
<img
class="BallotVerifierContest__Option_Image"
:src="parsedOption.image"
v-if="parsedOption.image"
/>
<div class="BallotVerifierContest__Option_Title">
{{ parsedOption.title }}
</div>
<AVOptionCheckbox
v-if="!isMultipleCrossesPerVote"
:rank="parsedOption.rank"
:checked="true"
:disabled="true"
class="BallotVerifierContest__Option_Cross"
/>
<div class="BallotVerifierContest__Option_Crosses" v-else>
<AVOptionCheckbox
v-for="(_, i) in parsedOption.count"
:checked="true"
:disabled="true"
:key="`cross-${i}`"
/>
</div>
</div>
</div>
<AVPileSummary
:contest="contest"
:selection-pile="selectionPile"
:pile-index="pileIndex"
:total-piles="contestSelection.piles.length"
active-state="summary"
/>
</div>
</div>
</template>

<script lang="ts">
import useConfigStore from "@/stores/useConfigStore";
import type { PropType } from "vue";
import { defineComponent } from "vue";
import type {
ContestSelection,
SelectionPile,
} from "@aion-dk/js-client/dist/lib/av_client/types";
import type { ContestSelection } from "@aion-dk/js-client/dist/lib/av_client/types";
import type { FullContestContent } from "@/Types";
export default defineComponent({
Expand All @@ -88,10 +34,6 @@ export default defineComponent({
type: Object as PropType<ContestSelection>,
required: true,
},
index: {
type: Number,
required: true,
},
},
computed: {
configStore() {
Expand All @@ -100,44 +42,6 @@ export default defineComponent({
contest(): FullContestContent {
return this.configStore.getContest(this.contestSelection.reference);
},
isRanked() {
return this.contest.markingType.voteVariation === "ranked";
},
isMultipleCrossesPerVote() {
return (
1 <
(this.contest.markingType.votesAllowedPerOption ||
this.contest.votesAllowedPerOption ||
1)
);
},
},
methods: {
parseOptions(pile: SelectionPile) {
let talliedReferences = pile.optionSelections.reduce(
(tally: any, option) => {
tally[option.reference] = 1 + (tally[option.reference] || 0);
return tally;
},
{}
);
return Object.entries(talliedReferences).map(
([optionReference, count], index) => {
const optionContent = this.configStore.getContestOption(
this.contest.reference,
optionReference
);
return {
title: optionContent.title[this.$i18n.locale],
count: count,
rank: this.isRanked ? index + 1 : 0,
image: optionContent.image,
};
}
);
},
},
});
</script>
Expand All @@ -156,77 +60,10 @@ export default defineComponent({
font-weight: 600;
}
.BallotVerifierContest__Pile {
margin-bottom: 1rem;
position: relative;
z-index: 10;
}
.BallotVerifierContest__Header {
.BallotVerifierContest__Piles {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem;
margin: 0;
background-color: var(--slate-200);
}
.BallotVerifierContest__Options {
display: grid;
grid-auto-flow: row;
grid-auto-rows: 1fr;
gap: 1rem;
&:not(:first-child) {
border: 1px solid var(--slate-200);
padding: 1rem;
}
}
.BallotVerifierContest__Option {
position: relative;
z-index: 10;
display: grid;
grid-template-areas: "image title cross" "crosses crosses crosses";
grid-template-columns: auto 1fr auto;
grid-template-rows: 1fr auto;
border: 1px #dddddd solid;
background-color: white;
align-items: center;
}
.BallotVerifierContest__Option_Image {
grid-area: image;
height: 4rem;
margin: 1rem;
}
.BallotVerifierContest__Option_Title {
grid-area: title;
align-self: center;
font-size: 1.25rem;
margin: 1rem;
&:not(:first-child) {
margin-left: 0;
}
}
.BallotVerifierContest__Title + p {
flex-direction: column;
margin-bottom: 1rem;
font-weight: 600;
}
.BallotVerifierContest__Option_Cross {
grid-area: cross;
margin: 1rem;
}
.BallotVerifierContest__Option_Crosses {
grid-area: crosses;
padding: 1rem;
background-color: #f8f9fa;
border-top: #dddddd 1px solid;
direction: rtl;
display: grid;
grid-template-columns: repeat(auto-fit, 30px);
gap: 0.5rem;
z-index: 10;
}
</style>
25 changes: 20 additions & 5 deletions client/src/lib/conferenceServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { ref } from "vue";
import { responseErrorHandler, responseHandler } from "./axiosConfig";
import config from "./config";

import type { ElectionStatusResponse } from "../Types";
import type {
ElectionStatusResponse,
CurrentTranslations,
SpreadableDLM,
} from "../Types";
import type { Locale } from "vue-i18n";
import type { AxiosInstance } from "axios";

const conferenceApi = ref<AxiosInstance>(
Expand All @@ -12,7 +17,7 @@ const conferenceApi = ref<AxiosInstance>(
})
);

const currentTranslationsData: any = ref(null);
const currentTranslationsData = ref<CurrentTranslations>(null);

export function useConferenceConnector(
organisationSlug: string,
Expand All @@ -36,15 +41,25 @@ export function useConferenceConnector(
`/${organisationSlug}/${electionSlug}/theme`
)) as string;
},
async getTranslationsData(locale: string) {
async getTranslationsData(locale: Locale) {
if (!currentTranslationsData.value) {
currentTranslationsData.value = await conferenceApi.value.get(
`/${organisationSlug}/${electionSlug}/translations`
);
}

return currentTranslationsData.value?.translations[locale].js
.election_verification_site;
const evsTranslations = {
...(currentTranslationsData.value?.translations[locale].js
.election_verification_site as SpreadableDLM),
js: {
components: {
...(currentTranslationsData.value?.translations[locale].js
.components as SpreadableDLM),
},
},
};

return evsTranslations;
},
},
};
Expand Down
Loading

0 comments on commit 7563a49

Please sign in to comment.