Improve i18n (#61)
- add support for RTL languages - fix bug with non-existent locale in localStorage - add globe icon to detect the language field
This commit is contained in:
commit
8304cf0f10
5 changed files with 32 additions and 24 deletions
|
@ -15,7 +15,10 @@ const { instance, errors } = Astro.props;
|
||||||
data-translate="instance"
|
data-translate="instance"
|
||||||
>
|
>
|
||||||
Fediverse instance
|
Fediverse instance
|
||||||
<div class="instance-input">
|
<div
|
||||||
|
class="instance-input"
|
||||||
|
dir="ltr"
|
||||||
|
>
|
||||||
<span id="https-label">https://</span>
|
<span id="https-label">https://</span>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -90,7 +93,7 @@ const { instance, errors } = Astro.props;
|
||||||
}
|
}
|
||||||
|
|
||||||
#saved-instances {
|
#saved-instances {
|
||||||
margin-bottom: 1rem;
|
margin-block-end: 1rem;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -11,8 +11,8 @@ import { languages } from "@i18n/translations";
|
||||||
const initialLanguage = "en";
|
const initialLanguage = "en";
|
||||||
---
|
---
|
||||||
|
|
||||||
<label data-translate="language">
|
<label>
|
||||||
Language:
|
🌍 <span data-translate="language">Language:</span>
|
||||||
<select
|
<select
|
||||||
name="language"
|
name="language"
|
||||||
id="language"
|
id="language"
|
||||||
|
@ -24,7 +24,7 @@ const initialLanguage = "en";
|
||||||
selected={k === initialLanguage}
|
selected={k === initialLanguage}
|
||||||
value={k}
|
value={k}
|
||||||
>
|
>
|
||||||
{v}
|
{v.autonym}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,9 +9,6 @@
|
||||||
import { strings, defaultLanguage, languages } from "./translations";
|
import { strings, defaultLanguage, languages } from "./translations";
|
||||||
|
|
||||||
export function useTranslations(language: string) {
|
export function useTranslations(language: string) {
|
||||||
if (!(language in strings)) {
|
|
||||||
language = defaultLanguage;
|
|
||||||
}
|
|
||||||
return function t(
|
return function t(
|
||||||
key: keyof (typeof strings)[typeof defaultLanguage],
|
key: keyof (typeof strings)[typeof defaultLanguage],
|
||||||
): string {
|
): string {
|
||||||
|
@ -46,6 +43,9 @@ export function findBestLanguage(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyTranslations(language: string) {
|
export function applyTranslations(language: string) {
|
||||||
|
if (!(language in strings)) {
|
||||||
|
language = defaultLanguage;
|
||||||
|
}
|
||||||
const t = useTranslations(language);
|
const t = useTranslations(language);
|
||||||
|
|
||||||
for (const node of document.querySelectorAll("[data-translate]")) {
|
for (const node of document.querySelectorAll("[data-translate]")) {
|
||||||
|
@ -71,4 +71,8 @@ export function applyTranslations(language: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.documentElement.lang = language;
|
||||||
|
document.documentElement.dir =
|
||||||
|
languages[language as keyof typeof languages].dir;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ import nl from "./translations/nl.json";
|
||||||
import ru from "./translations/ru.json";
|
import ru from "./translations/ru.json";
|
||||||
|
|
||||||
export const languages = {
|
export const languages = {
|
||||||
en: "English",
|
en: { autonym: "English", dir: "ltr" },
|
||||||
de: "Deutsch",
|
de: { autonym: "Deutsch", dir: "ltr" },
|
||||||
es: "Español",
|
es: { autonym: "Español", dir: "ltr" },
|
||||||
fr: "Français",
|
fr: { autonym: "Français", dir: "ltr" },
|
||||||
nl: "Nederlands",
|
nl: { autonym: "Nederlands", dir: "ltr" },
|
||||||
ru: "Русский",
|
ru: { autonym: "Русский", dir: "ltr" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const strings: Record<keyof typeof languages, Record<string, string>> = {
|
export const strings: Record<keyof typeof languages, Record<string, string>> = {
|
||||||
|
|
|
@ -39,7 +39,8 @@ html {
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 60em;
|
max-width: 60em;
|
||||||
margin: 0 auto;
|
margin-block: 0;
|
||||||
|
margin-inline: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
|
|
||||||
|
@ -55,19 +56,16 @@ header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
> div {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
margin-bottom: 2rem;
|
margin-block-end: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main,
|
main,
|
||||||
aside {
|
aside {
|
||||||
padding: 0 1rem;
|
padding-block: 0;
|
||||||
|
padding-inline: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
|
@ -110,6 +108,7 @@ textarea {
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
resize: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
|
@ -130,7 +129,8 @@ input[type="submit"] {
|
||||||
color: var(--s2f-button-text-color);
|
color: var(--s2f-button-text-color);
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
padding: 0.5rem 1.5rem;
|
padding-block: 0.5rem;
|
||||||
|
padding-inline: 1.5rem;
|
||||||
border: 0;
|
border: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
appearance: button;
|
appearance: button;
|
||||||
|
@ -175,9 +175,10 @@ input[type="submit"] {
|
||||||
|
|
||||||
p.error {
|
p.error {
|
||||||
color: var(--s2f-error-color);
|
color: var(--s2f-error-color);
|
||||||
margin: 0 0 1rem;
|
margin-block: 0 1rem;
|
||||||
|
margin-inline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt1r {
|
.mt1r {
|
||||||
margin-top: 1rem !important;
|
margin-block-start: 1rem !important;
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue