feat(client): add Portuguese language localization (#688)

Expose Portuguese through shared locale preferences and keep the catalog synchronized with current English messages. Use keyed interpolation so series headings follow each language’s natural word order.

Closes #673

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
This commit is contained in:
zanivann
2026-07-14 14:53:16 -03:00
committed by GitHub
parent ec84703cef
commit 6544d23002
8 changed files with 6264 additions and 14 deletions
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { watch, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useSeriesBooks } from '@/features/book/composables/useSeriesBooks'
import BookCarousel from '@/features/book/components/detail/BookCarousel.vue'
@@ -10,7 +9,6 @@ const props = defineProps<{
seriesName: string
}>()
const { t } = useI18n()
const { seriesBooks, loading, fetch } = useSeriesBooks()
watch(
@@ -29,8 +27,11 @@ defineExpose({ bookIds })
<BookCarousel :books="seriesBooks" :loading="loading" :current-book-id="bookId" :show-series-index="true">
<template #header>
<p class="text-[11px] font-semibold uppercase tracking-widest text-muted-foreground">
{{ t('book.detail.series.moreInSeriesPrefix') }} <span class="italic">{{ seriesName }}</span>
{{ t('book.detail.series.moreInSeriesSuffix') }}
<i18n-t keypath="book.detail.series.moreInSeries" scope="global">
<template #series>
<span class="italic">{{ seriesName }}</span>
</template>
</i18n-t>
</p>
</template>
</BookCarousel>
+1 -2
View File
@@ -3095,8 +3095,7 @@
"moreLikeThis": "Mehr davon"
},
"series": {
"moreInSeriesPrefix": "Mehr aus der",
"moreInSeriesSuffix": "Reihe"
"moreInSeries": "Mehr aus der Reihe {series}"
},
"writeRename": {
"written": "keine Felder geschrieben | Geschrieben (ein Feld) | Geschrieben ({count} Felder)",
+1 -2
View File
@@ -3089,8 +3089,7 @@
"moreLikeThis": "More Like This"
},
"series": {
"moreInSeriesPrefix": "More in the",
"moreInSeriesSuffix": "series"
"moreInSeries": "More in the {series} series"
},
"writeRename": {
"written": "no fields written | Written (one field) | Written ({count} fields)",
+1 -2
View File
@@ -3100,8 +3100,7 @@
"moreLikeThis": "Meer zoals dit"
},
"series": {
"moreInSeriesPrefix": "Meer in de",
"moreInSeriesSuffix": "reeks"
"moreInSeries": "Meer in de reeks {series}"
},
"writeRename": {
"written": "geen velden geschreven | Geschreven (één veld) | Geschreven ({count} velden)",
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -3092,8 +3092,7 @@
"moreLikeThis": "Več podobnega"
},
"series": {
"moreInSeriesPrefix": "Več v",
"moreInSeriesSuffix": "zbirki"
"moreInSeries": "Več v zbirki {series}"
},
"writeRename": {
"written": "ni zapisanih polj | Zapisano (eno polje) | Zapisano ({count} polj)",
+1 -1
View File
@@ -32,7 +32,7 @@ describe('locale store', () => {
expect(matchSupportedLocale(['nl-NL', 'en-US'])).toBe('nl')
expect(matchSupportedLocale(['de-DE', 'en-GB'])).toBe('de')
expect(matchSupportedLocale(['nl-BE', 'en'])).toBe('nl')
expect(matchSupportedLocale(['pt-BR'])).toBeNull()
expect(matchSupportedLocale(['pt-BR'])).toBe('pt')
})
it('prefers the stored locale over browser detection', async () => {
+3 -1
View File
@@ -1,4 +1,4 @@
export const SUPPORTED_LOCALES = ["en", "de", "nl", "sl"] as const;
export const SUPPORTED_LOCALES = ["en", "de", "nl", "pt", "sl"] as const;
export type Locale = (typeof SUPPORTED_LOCALES)[number];
export const DEFAULT_LOCALE: Locale = "en";
@@ -8,6 +8,7 @@ export const LOCALE_LABELS: Record<Locale, string> = {
en: "English",
de: "Deutsch",
nl: "Nederlands",
pt: "Português",
sl: "Slovenščina",
};
@@ -15,6 +16,7 @@ export const LOCALE_DIRECTIONS: Record<Locale, "ltr" | "rtl"> = {
en: "ltr",
de: "ltr",
nl: "ltr",
pt: "ltr",
sl: "ltr",
};