Mobile dropdown: visibility and readability improvements; i18n locales filtered to fully translated; dynamic dropdown locales
This commit is contained in:
81
src/App.vue
81
src/App.vue
@@ -13,7 +13,7 @@ import FixedBar from './components/FixedBar.vue';
|
|||||||
|
|
||||||
// Main App Entry
|
// Main App Entry
|
||||||
const store = usePuzzleStore();
|
const store = usePuzzleStore();
|
||||||
const { t, locale, setLocale } = useI18n();
|
const { t, locale, setLocale, locales } = useI18n();
|
||||||
const showCustomModal = ref(false);
|
const showCustomModal = ref(false);
|
||||||
const showGuide = ref(false);
|
const showGuide = ref(false);
|
||||||
const deferredPrompt = ref(null);
|
const deferredPrompt = ref(null);
|
||||||
@@ -74,53 +74,9 @@ const languageFlags = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const languages = computed(() => {
|
const languages = computed(() => {
|
||||||
const items = [
|
return locales.value
|
||||||
{ code: 'en', label: t('language.en') },
|
.map((code) => ({ code, label: t(`language.${code}`) }))
|
||||||
{ code: 'zh', label: t('language.zh') },
|
.sort((a, b) => a.label.localeCompare(b.label, locale.value));
|
||||||
{ code: 'hi', label: t('language.hi') },
|
|
||||||
{ code: 'es', label: t('language.es') },
|
|
||||||
{ code: 'fr', label: t('language.fr') },
|
|
||||||
{ code: 'ar', label: t('language.ar') },
|
|
||||||
{ code: 'bn', label: t('language.bn') },
|
|
||||||
{ code: 'ru', label: t('language.ru') },
|
|
||||||
{ code: 'pt', label: t('language.pt') },
|
|
||||||
{ code: 'ur', label: t('language.ur') },
|
|
||||||
{ code: 'pl', label: t('language.pl') },
|
|
||||||
{ code: 'de', label: t('language.de') },
|
|
||||||
{ code: 'it', label: t('language.it') },
|
|
||||||
{ code: 'nl', label: t('language.nl') },
|
|
||||||
{ code: 'sv', label: t('language.sv') },
|
|
||||||
{ code: 'da', label: t('language.da') },
|
|
||||||
{ code: 'fi', label: t('language.fi') },
|
|
||||||
{ code: 'no', label: t('language.no') },
|
|
||||||
{ code: 'cs', label: t('language.cs') },
|
|
||||||
{ code: 'sk', label: t('language.sk') },
|
|
||||||
{ code: 'hu', label: t('language.hu') },
|
|
||||||
{ code: 'ro', label: t('language.ro') },
|
|
||||||
{ code: 'bg', label: t('language.bg') },
|
|
||||||
{ code: 'el', label: t('language.el') },
|
|
||||||
{ code: 'uk', label: t('language.uk') },
|
|
||||||
{ code: 'be', label: t('language.be') },
|
|
||||||
{ code: 'sr', label: t('language.sr') },
|
|
||||||
{ code: 'hr', label: t('language.hr') },
|
|
||||||
{ code: 'sl', label: t('language.sl') },
|
|
||||||
{ code: 'lt', label: t('language.lt') },
|
|
||||||
{ code: 'lv', label: t('language.lv') },
|
|
||||||
{ code: 'et', label: t('language.et') },
|
|
||||||
{ code: 'ga', label: t('language.ga') },
|
|
||||||
{ code: 'is', label: t('language.is') },
|
|
||||||
{ code: 'mt', label: t('language.mt') },
|
|
||||||
{ code: 'sq', label: t('language.sq') },
|
|
||||||
{ code: 'mk', label: t('language.mk') },
|
|
||||||
{ code: 'bs', label: t('language.bs') },
|
|
||||||
{ code: 'tr', label: t('language.tr') },
|
|
||||||
{ code: 'ca', label: t('language.ca') },
|
|
||||||
{ code: 'gl', label: t('language.gl') },
|
|
||||||
{ code: 'cy', label: t('language.cy') },
|
|
||||||
{ code: 'gd', label: t('language.gd') },
|
|
||||||
{ code: 'eu', label: t('language.eu') }
|
|
||||||
];
|
|
||||||
return items.sort((a, b) => a.label.localeCompare(b.label, locale.value));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const installLabel = computed(() => {
|
const installLabel = computed(() => {
|
||||||
@@ -574,6 +530,23 @@ h1 {
|
|||||||
font-size: 2.4rem;
|
font-size: 2.4rem;
|
||||||
letter-spacing: 3px;
|
letter-spacing: 3px;
|
||||||
}
|
}
|
||||||
|
.lang-menu {
|
||||||
|
position: fixed;
|
||||||
|
top: 64px;
|
||||||
|
left: 50%;
|
||||||
|
right: auto;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: min(92vw, 340px);
|
||||||
|
min-width: 240px;
|
||||||
|
z-index: 1005;
|
||||||
|
max-height: calc(100vh - 96px);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
|
.lang-option {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 420px) {
|
@media (max-width: 420px) {
|
||||||
@@ -582,17 +555,7 @@ h1 {
|
|||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
}
|
}
|
||||||
.lang-menu {
|
.lang-menu {
|
||||||
position: fixed;
|
width: min(94vw, 360px);
|
||||||
top: 64px;
|
|
||||||
left: 50%;
|
|
||||||
right: auto;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
width: min(90vw, 320px);
|
|
||||||
min-width: 240px;
|
|
||||||
z-index: 1005;
|
|
||||||
max-height: calc(100vh - 96px);
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 18px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
const supportedLocales = [
|
|
||||||
'pl','en','zh','hi','es','fr','ar','bn','ru','pt','ur',
|
|
||||||
'de','it','nl','sv','da','fi','no','cs','sk','hu','ro','bg','el','uk','be',
|
|
||||||
'sr','hr','sl','lt','lv','et','ga','is','mt','sq','mk','bs','tr','ca','gl','cy','gd','eu'
|
|
||||||
];
|
|
||||||
|
|
||||||
const detectLocale = () => {
|
|
||||||
if (typeof navigator === 'undefined') return 'en';
|
|
||||||
const browserLocale = (navigator.languages && navigator.languages[0]) || navigator.language || 'en';
|
|
||||||
const short = browserLocale.toLowerCase().split('-')[0];
|
|
||||||
return supportedLocales.includes(short) ? short : 'en';
|
|
||||||
};
|
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
pl: {
|
pl: {
|
||||||
'app.title': 'Nonograms',
|
'app.title': 'Nonograms',
|
||||||
@@ -787,6 +774,28 @@ const messages = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const requiredKeys = [
|
||||||
|
'app.title','level.easy','level.medium','level.hard','level.custom','level.guide',
|
||||||
|
'actions.reset','actions.random','actions.undo','status.time','status.moves','status.progress',
|
||||||
|
'fixed.time','fixed.progress','fixed.hide','fixed.show','guide.play','guide.pause','guide.step',
|
||||||
|
'guide.speed','guide.waiting','guide.solved','custom.title','custom.prompt','custom.cancel',
|
||||||
|
'custom.start','custom.sizeError','win.title','win.message','win.time','win.playAgain',
|
||||||
|
'win.shareTitle','win.shareText','win.shareX','win.shareFacebook','win.shareWhatsapp',
|
||||||
|
'win.shareDownload','pwa.installTitle','pwa.installMobile','pwa.installDesktop',
|
||||||
|
'language.label','theme.label','theme.system','theme.light','theme.dark'
|
||||||
|
];
|
||||||
|
|
||||||
|
const supportedLocales = Object.keys(messages).filter(
|
||||||
|
(code) => requiredKeys.every((k) => messages[code] && messages[code][k])
|
||||||
|
);
|
||||||
|
|
||||||
|
const detectLocale = () => {
|
||||||
|
if (typeof navigator === 'undefined') return 'en';
|
||||||
|
const browserLocale = (navigator.languages && navigator.languages[0]) || navigator.language || 'en';
|
||||||
|
const short = browserLocale.toLowerCase().split('-')[0];
|
||||||
|
return supportedLocales.includes(short) ? short : 'en';
|
||||||
|
};
|
||||||
|
|
||||||
const locale = ref(detectLocale());
|
const locale = ref(detectLocale());
|
||||||
|
|
||||||
const format = (text, params = {}) => {
|
const format = (text, params = {}) => {
|
||||||
@@ -819,6 +828,7 @@ export function useI18n() {
|
|||||||
return {
|
return {
|
||||||
locale: computed(() => locale.value),
|
locale: computed(() => locale.value),
|
||||||
t,
|
t,
|
||||||
setLocale
|
setLocale,
|
||||||
|
locales: computed(() => supportedLocales)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user