include translations for about buttons and implement translation fallback if used translation is not complete

This commit is contained in:
schlagmichdoch 2023-07-07 14:58:15 +02:00
parent f50d7438b6
commit da5038a51a
6 changed files with 78 additions and 52 deletions

View file

@ -83,7 +83,7 @@
<use xlink:href="#edit-pair-devices-icon" />
</svg>
</div>
<div id="cancel-paste-mode" class="button" data-i18n-key="header.done" data-i18n-attrs="text" hidden>Done</div>
<div id="cancel-paste-mode" class="button" data-i18n-key="header.cancel-paste-mode" data-i18n-attrs="text" hidden>Done</div>
</header>
<!-- Center -->
<div id="center">
@ -161,7 +161,7 @@
<x-background class="full center text-center">
<x-paper shadow="2">
<h2 class="center" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text">Edit Paired Devices</h2>
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-empty" data-i18n-attrs="data-empty" data-empty="No paired devices."></div>
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-wrapper" data-i18n-attrs="data-empty" data-empty="No paired devices."></div>
<div class="font-subheading center">
<p>
<span data-i18n-key="dialogs.auto-accept-instructions-1" data-i18n-attrs="text">
@ -287,7 +287,7 @@
<!-- About Page -->
<x-about id="about" class="full center column">
<header class="row-reverse fade-in">
<a href="#" class="close icon-button" data-i18n-key="about.close-about" data-i18n-attrs="text" aria-label="Close About PairDrop">
<a href="#" class="close icon-button" data-i18n-key="about.close-about" data-i18n-attrs="aria-label" aria-label="Close About PairDrop">
<svg class="icon">
<use xlink:href="#close-icon" />
</svg>
@ -303,22 +303,22 @@
</div>
<div class="font-subheading" data-i18n-key="about.claim" data-i18n-attrs="text">The easiest way to transfer files across devices</div>
<div class="row">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop" title="PairDrop on Github" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop" title="PairDrop on Github" rel="noreferrer" data-i18n-key="about.github" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#github" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://www.buymeacoffee.com/pairdrop" title="Buy me a coffee!" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://www.buymeacoffee.com/pairdrop" title="Buy me a coffee!" rel="noreferrer" data-i18n-key="about.buy-me-a-coffee" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#monetarization" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://twitter.com/intent/tweet?text=https%3A%2F%2Fpairdrop.net%20by%20https%3A%2F%2Fgithub.com%2Fschlagmichdoch%2F&" title="Tweet about PairDrop" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://twitter.com/intent/tweet?text=https%3A%2F%2Fpairdrop.net%20by%20https%3A%2F%2Fgithub.com%2Fschlagmichdoch%2F&" title="Tweet about PairDrop" rel="noreferrer" data-i18n-key="about.tweet" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#twitter" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop/blob/master/docs/faq.md" title="Frequently asked questions" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop/blob/master/docs/faq.md" title="Frequently asked questions" rel="noreferrer" data-i18n-key="about.faq" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#help-outline" />
</svg>

View file

@ -78,8 +78,12 @@
"download-again": "Download again"
},
"about": {
"close-about-aria-label": "Close About PairDrop",
"claim": "The easiest way to transfer files across devices"
"close-about_aria-label": "Close About PairDrop",
"claim": "The easiest way to transfer files across devices",
"github_title": "PairDrop on Github",
"buy-me-a-coffee_title": "Buy me a coffee!",
"tweet_title": "Tweet about PairDrop",
"faq_title": "Frequently asked questions"
},
"notifications": {
"display-name-changed-permanently": "Display name is changed permanently.",

View file

@ -2,10 +2,10 @@ class Localization {
constructor() {
Localization.defaultLocale = "en";
Localization.supportedLocales = ["en"];
Localization.translations = {};
Localization.defaultTranslations = {};
const initialLocale = Localization.supportedOrDefault(Localization.browserLocales());
const initialLocale = Localization.supportedOrDefault(navigator.languages);
Localization.setLocale(initialLocale)
.then(_ => {
@ -21,25 +21,21 @@ class Localization {
return locales.find(Localization.isSupported) || Localization.defaultLocale;
}
static browserLocales() {
return navigator.languages.map(locale =>
locale.split("-")[0]
);
}
static async setLocale(newLocale) {
if (newLocale === Localization.locale) return false;
const isFirstTranslation = !Localization.locale
Localization.defaultTranslations = await Localization.fetchTranslationsFor(Localization.defaultLocale);
const newTranslations = await Localization.fetchTranslationsFor(newLocale);
if(!newTranslations) return false;
const firstTranslation = !Localization.locale
Localization.locale = newLocale;
Localization.translations = newTranslations;
if (firstTranslation) {
if (isFirstTranslation) {
Events.fire("translation-loaded");
}
}
@ -65,30 +61,43 @@ class Localization {
for (let i in attrs) {
let attr = attrs[i];
if (attr === "text") {
element.innerText = await Localization.getTranslation(key);
element.innerText = Localization.getTranslation(key);
} else {
element.attr = await Localization.getTranslation(key, attr);
element.attr = Localization.getTranslation(key, attr);
}
}
}
static getTranslation(key, attr, data) {
static getTranslation(key, attr, data, useDefault=false) {
const keys = key.split(".");
let translationCandidates = Localization.translations;
let translationCandidates = useDefault
? Localization.defaultTranslations
: Localization.translations;
for (let i=0; i<keys.length-1; i++) {
translationCandidates = translationCandidates[keys[i]]
}
let lastKey = keys[keys.length-1];
if (attr) lastKey += "_" + attr;
let translation = translationCandidates[lastKey];
for (key in data) {
translation = translation.replace(`{{${key}}}`, data[key]);
for (let j in data) {
translation = translation.replace(`{{${j}}}`, data[j]);
}
if (!translation) {
if (!useDefault) {
translation = this.getTranslation(key, attr, data, true);
console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr);
console.warn("Help translating PairDrop: https://hosted.weblate.org/projects/pairdrop/pairdrop-spa/");
} else {
console.warn("Missing translation in default language:", key, attr);
}
}
return Localization.escapeHTML(translation);

View file

@ -83,7 +83,7 @@
<use xlink:href="#edit-pair-devices-icon" />
</svg>
</div>
<div id="cancel-paste-mode" class="button" data-i18n-key="header.done" data-i18n-attrs="text" hidden>Done</div>
<div id="cancel-paste-mode" class="button" data-i18n-key="header.cancel-paste-mode" data-i18n-attrs="text" hidden>Done</div>
</header>
<!-- Center -->
<div id="center">
@ -166,7 +166,7 @@
<x-background class="full center text-center">
<x-paper shadow="2">
<h2 class="center" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text">Edit Paired Devices</h2>
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-empty" data-i18n-attrs="data-empty" data-empty="No paired devices."></div>
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-wrapper" data-i18n-attrs="data-empty" data-empty="No paired devices."></div>
<div class="font-subheading center">
<p>
<span data-i18n-key="dialogs.auto-accept-instructions-1" data-i18n-attrs="text">
@ -292,7 +292,7 @@
<!-- About Page -->
<x-about id="about" class="full center column">
<header class="row-reverse fade-in">
<a href="#" class="close icon-button" data-i18n-key="about.close-about" data-i18n-attrs="text" aria-label="Close About PairDrop">
<a href="#" class="close icon-button" data-i18n-key="about.close-about" data-i18n-attrs="aria-label" aria-label="Close About PairDrop">
<svg class="icon">
<use xlink:href="#close-icon" />
</svg>
@ -308,22 +308,22 @@
</div>
<div class="font-subheading" data-i18n-key="about.claim" data-i18n-attrs="text">The easiest way to transfer files across devices</div>
<div class="row">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop" title="PairDrop on Github" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop" title="PairDrop on Github" rel="noreferrer" data-i18n-key="about.github" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#github" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://www.buymeacoffee.com/pairdrop" title="Buy me a coffee!" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://www.buymeacoffee.com/pairdrop" title="Buy me a coffee!" rel="noreferrer" data-i18n-key="about.buy-me-a-coffee" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#monetarization" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://twitter.com/intent/tweet?text=https%3A%2F%2Fpairdrop.net%20by%20https%3A%2F%2Fgithub.com%2Fschlagmichdoch%2F&" title="Tweet about PairDrop" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://twitter.com/intent/tweet?text=https%3A%2F%2Fpairdrop.net%20by%20https%3A%2F%2Fgithub.com%2Fschlagmichdoch%2F&" title="Tweet about PairDrop" rel="noreferrer" data-i18n-key="about.tweet" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#twitter" />
</svg>
</a>
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop/blob/master/docs/faq.md" title="Frequently asked questions" rel="noreferrer">
<a class="icon-button" target="_blank" href="https://github.com/schlagmichdoch/pairdrop/blob/master/docs/faq.md" title="Frequently asked questions" rel="noreferrer" data-i18n-key="about.faq" data-i18n-attrs="title">
<svg class="icon">
<use xlink:href="#help-outline" />
</svg>

View file

@ -78,8 +78,12 @@
"download-again": "Download again"
},
"about": {
"close-about-aria-label": "Close About PairDrop",
"claim": "The easiest way to transfer files across devices"
"close-about_aria-label": "Close About PairDrop",
"claim": "The easiest way to transfer files across devices",
"github_title": "PairDrop on Github",
"buy-me-a-coffee_title": "Buy me a coffee!",
"tweet_title": "Tweet about PairDrop",
"faq_title": "Frequently asked questions"
},
"notifications": {
"display-name-changed-permanently": "Display name is changed permanently.",

View file

@ -2,10 +2,10 @@ class Localization {
constructor() {
Localization.defaultLocale = "en";
Localization.supportedLocales = ["en"];
Localization.translations = {};
Localization.defaultTranslations = {};
const initialLocale = Localization.supportedOrDefault(Localization.browserLocales());
const initialLocale = Localization.supportedOrDefault(navigator.languages);
Localization.setLocale(initialLocale)
.then(_ => {
@ -21,25 +21,21 @@ class Localization {
return locales.find(Localization.isSupported) || Localization.defaultLocale;
}
static browserLocales() {
return navigator.languages.map(locale =>
locale.split("-")[0]
);
}
static async setLocale(newLocale) {
if (newLocale === Localization.locale) return false;
const isFirstTranslation = !Localization.locale
Localization.defaultTranslations = await Localization.fetchTranslationsFor(Localization.defaultLocale);
const newTranslations = await Localization.fetchTranslationsFor(newLocale);
if(!newTranslations) return false;
const firstTranslation = !Localization.locale
Localization.locale = newLocale;
Localization.translations = newTranslations;
if (firstTranslation) {
if (isFirstTranslation) {
Events.fire("translation-loaded");
}
}
@ -65,30 +61,43 @@ class Localization {
for (let i in attrs) {
let attr = attrs[i];
if (attr === "text") {
element.innerText = await Localization.getTranslation(key);
element.innerText = Localization.getTranslation(key);
} else {
element.attr = await Localization.getTranslation(key, attr);
element.attr = Localization.getTranslation(key, attr);
}
}
}
static getTranslation(key, attr, data) {
static getTranslation(key, attr, data, useDefault=false) {
const keys = key.split(".");
let translationCandidates = Localization.translations;
let translationCandidates = useDefault
? Localization.defaultTranslations
: Localization.translations;
for (let i=0; i<keys.length-1; i++) {
translationCandidates = translationCandidates[keys[i]]
}
let lastKey = keys[keys.length-1];
if (attr) lastKey += "_" + attr;
let translation = translationCandidates[lastKey];
for (key in data) {
translation = translation.replace(`{{${key}}}`, data[key]);
for (let j in data) {
translation = translation.replace(`{{${j}}}`, data[j]);
}
if (!translation) {
if (!useDefault) {
translation = this.getTranslation(key, attr, data, true);
console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr);
console.warn("Help translating PairDrop: https://hosted.weblate.org/projects/pairdrop/pairdrop-spa/");
} else {
console.warn("Missing translation in default language:", key, attr);
}
}
return Localization.escapeHTML(translation);