Some i18n stuff
This commit is contained in:
@@ -208,6 +208,9 @@
|
|||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
"archive": "Are you sure you want to archive this spool?"
|
"archive": "Are you sure you want to archive this spool?"
|
||||||
|
},
|
||||||
|
"formats": {
|
||||||
|
"last_used": "Last used {{date}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filament": {
|
"filament": {
|
||||||
@@ -377,5 +380,9 @@
|
|||||||
"create": "Create Manufacturer | Spoolman",
|
"create": "Create Manufacturer | Spoolman",
|
||||||
"clone": "#{{id}} Clone Manufacturer | Spoolman"
|
"clone": "#{{id}} Clone Manufacturer | Spoolman"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"locations": {
|
||||||
|
"new_location": "New Location",
|
||||||
|
"no_location": "No Location"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,9 @@
|
|||||||
"measurement_type_label": "Mättyp",
|
"measurement_type_label": "Mättyp",
|
||||||
"adjust_filament_value": "Mängd att konsumera",
|
"adjust_filament_value": "Mängd att konsumera",
|
||||||
"adjust_filament_help": "Här kan du direkt lägga till eller subtrahera filament från spolen. Ett positivt värde kommer att konsumera filament, ett negativt värde kommer att lägga till det."
|
"adjust_filament_help": "Här kan du direkt lägga till eller subtrahera filament från spolen. Ett positivt värde kommer att konsumera filament, ett negativt värde kommer att lägga till det."
|
||||||
|
},
|
||||||
|
"formats": {
|
||||||
|
"last_used": "Senast använd {{date}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filament": {
|
"filament": {
|
||||||
@@ -376,5 +379,9 @@
|
|||||||
"description": "<title>Hjälp</title><p>Här är några tips för att komma igång.</p><p>Spoolman innehåller 3 olika typer av data:</p><itemsHelp/><p>För att lägga till en ny spole i databasen måste du först skapa ett <filamentCreateLink>Filament</filamentCreateLink>-objekt för den. När det är klart kan du sedan skapa ett <spoolCreateLink>Spol</spoolCreateLink>-objekt för den individuella spolen. Om du sedan köper ytterligare spolar av samma filament kan du bara skapa ytterligare Spol-objekt och återanvända samma Filament-objekt.</p><p>Du kan valfritt också skapa ett <vendorCreateLink>Tillverkar</vendorCreateLink>-objekt för företaget som tillverkar filamentet om du vill spåra den informationen.</p><p>Du kan koppla andra 3D-skrivartjänster till Spoolman, som Moonraker, som sedan automatiskt kan spåra filamentanvändning och uppdatera Spol-objekten åt dig. Se <readmeLink>Spoolman README</readmeLink> för hur du gör det.</p>",
|
"description": "<title>Hjälp</title><p>Här är några tips för att komma igång.</p><p>Spoolman innehåller 3 olika typer av data:</p><itemsHelp/><p>För att lägga till en ny spole i databasen måste du först skapa ett <filamentCreateLink>Filament</filamentCreateLink>-objekt för den. När det är klart kan du sedan skapa ett <spoolCreateLink>Spol</spoolCreateLink>-objekt för den individuella spolen. Om du sedan köper ytterligare spolar av samma filament kan du bara skapa ytterligare Spol-objekt och återanvända samma Filament-objekt.</p><p>Du kan valfritt också skapa ett <vendorCreateLink>Tillverkar</vendorCreateLink>-objekt för företaget som tillverkar filamentet om du vill spåra den informationen.</p><p>Du kan koppla andra 3D-skrivartjänster till Spoolman, som Moonraker, som sedan automatiskt kan spåra filamentanvändning och uppdatera Spol-objekten åt dig. Se <readmeLink>Spoolman README</readmeLink> för hur du gör det.</p>",
|
||||||
"help": "Hjälp"
|
"help": "Hjälp"
|
||||||
},
|
},
|
||||||
"kofi": "Dricksa mig på Ko-fi"
|
"kofi": "Dricksa mig på Ko-fi",
|
||||||
|
"locations": {
|
||||||
|
"new_location": "Ny plats",
|
||||||
|
"no_location": "Ingen plats"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import detector from "i18next-browser-languagedetector";
|
import detector from "i18next-browser-languagedetector";
|
||||||
import Backend from "i18next-http-backend";
|
import Backend from "i18next-http-backend";
|
||||||
@@ -8,6 +9,7 @@ interface Language {
|
|||||||
name: string;
|
name: string;
|
||||||
countryCode: string;
|
countryCode: string;
|
||||||
fullCode: string;
|
fullCode: string;
|
||||||
|
djs: () => Promise<ILocale>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,107 +18,128 @@ interface Language {
|
|||||||
* name: Name of the language in the list
|
* name: Name of the language in the list
|
||||||
* countryCode: Country code of the country's flag to display for this language
|
* countryCode: Country code of the country's flag to display for this language
|
||||||
* fullCode: Full language code, used for Ant Design's locale
|
* fullCode: Full language code, used for Ant Design's locale
|
||||||
|
* djs: Function to load the dayjs locale, see https://github.com/iamkun/dayjs/tree/dev/src/locale for list of locales
|
||||||
*/
|
*/
|
||||||
export const languages: { [key: string]: Language } = {
|
export const languages: { [key: string]: Language } = {
|
||||||
["en"]: {
|
["en"]: {
|
||||||
name: "English",
|
name: "English",
|
||||||
countryCode: "gb",
|
countryCode: "gb",
|
||||||
fullCode: "en-GB",
|
fullCode: "en-GB",
|
||||||
|
djs: () => import("dayjs/locale/en"),
|
||||||
},
|
},
|
||||||
["sv"]: {
|
["sv"]: {
|
||||||
name: "Svenska",
|
name: "Svenska",
|
||||||
countryCode: "se",
|
countryCode: "se",
|
||||||
fullCode: "sv-SE",
|
fullCode: "sv-SE",
|
||||||
|
djs: () => import("dayjs/locale/sv"),
|
||||||
},
|
},
|
||||||
["de"]: {
|
["de"]: {
|
||||||
name: "Deutsch",
|
name: "Deutsch",
|
||||||
countryCode: "de",
|
countryCode: "de",
|
||||||
fullCode: "de-DE",
|
fullCode: "de-DE",
|
||||||
|
djs: () => import("dayjs/locale/de"),
|
||||||
},
|
},
|
||||||
["es"]: {
|
["es"]: {
|
||||||
name: "Español",
|
name: "Español",
|
||||||
countryCode: "es",
|
countryCode: "es",
|
||||||
fullCode: "es-ES",
|
fullCode: "es-ES",
|
||||||
|
djs: () => import("dayjs/locale/es"),
|
||||||
},
|
},
|
||||||
["zh"]: {
|
["zh"]: {
|
||||||
name: "简体中文",
|
name: "简体中文",
|
||||||
countryCode: "cn",
|
countryCode: "cn",
|
||||||
fullCode: "zh-CN",
|
fullCode: "zh-CN",
|
||||||
|
djs: () => import("dayjs/locale/zh-cn"),
|
||||||
},
|
},
|
||||||
["zh-Hant"]: {
|
["zh-Hant"]: {
|
||||||
name: "繁體中文",
|
name: "繁體中文",
|
||||||
countryCode: "cn",
|
countryCode: "cn",
|
||||||
fullCode: "zh-TW",
|
fullCode: "zh-TW",
|
||||||
|
djs: () => import("dayjs/locale/zh-hk"),
|
||||||
},
|
},
|
||||||
["pl"]: {
|
["pl"]: {
|
||||||
name: "Polski",
|
name: "Polski",
|
||||||
countryCode: "pl",
|
countryCode: "pl",
|
||||||
fullCode: "pl-PL",
|
fullCode: "pl-PL",
|
||||||
|
djs: () => import("dayjs/locale/pl"),
|
||||||
},
|
},
|
||||||
["ru"]: {
|
["ru"]: {
|
||||||
name: "Русский",
|
name: "Русский",
|
||||||
countryCode: "ru",
|
countryCode: "ru",
|
||||||
fullCode: "ru-RU",
|
fullCode: "ru-RU",
|
||||||
|
djs: () => import("dayjs/locale/ru"),
|
||||||
},
|
},
|
||||||
["cs"]: {
|
["cs"]: {
|
||||||
name: "Česky",
|
name: "Česky",
|
||||||
countryCode: "cz",
|
countryCode: "cz",
|
||||||
fullCode: "cs-CZ",
|
fullCode: "cs-CZ",
|
||||||
|
djs: () => import("dayjs/locale/cs"),
|
||||||
},
|
},
|
||||||
["nb-NO"]: {
|
["nb-NO"]: {
|
||||||
name: "Norsk bokmål",
|
name: "Norsk bokmål",
|
||||||
countryCode: "no",
|
countryCode: "no",
|
||||||
fullCode: "nb-NO",
|
fullCode: "nb-NO",
|
||||||
|
djs: () => import("dayjs/locale/nb"),
|
||||||
},
|
},
|
||||||
["nl"]: {
|
["nl"]: {
|
||||||
name: "Nederlands",
|
name: "Nederlands",
|
||||||
countryCode: "nl",
|
countryCode: "nl",
|
||||||
fullCode: "nl-NL",
|
fullCode: "nl-NL",
|
||||||
|
djs: () => import("dayjs/locale/nl"),
|
||||||
},
|
},
|
||||||
["fr"]: {
|
["fr"]: {
|
||||||
name: "Français",
|
name: "Français",
|
||||||
countryCode: "fr",
|
countryCode: "fr",
|
||||||
fullCode: "fr-FR",
|
fullCode: "fr-FR",
|
||||||
|
djs: () => import("dayjs/locale/fr"),
|
||||||
},
|
},
|
||||||
["hu"]: {
|
["hu"]: {
|
||||||
name: "Magyar",
|
name: "Magyar",
|
||||||
countryCode: "hu",
|
countryCode: "hu",
|
||||||
fullCode: "hu-HU",
|
fullCode: "hu-HU",
|
||||||
|
djs: () => import("dayjs/locale/hu"),
|
||||||
},
|
},
|
||||||
["it"]: {
|
["it"]: {
|
||||||
name: "Italiano",
|
name: "Italiano",
|
||||||
countryCode: "it",
|
countryCode: "it",
|
||||||
fullCode: "it-IT",
|
fullCode: "it-IT",
|
||||||
|
djs: () => import("dayjs/locale/it"),
|
||||||
},
|
},
|
||||||
["uk"]: {
|
["uk"]: {
|
||||||
name: "Українська",
|
name: "Українська",
|
||||||
countryCode: "ua",
|
countryCode: "ua",
|
||||||
fullCode: "uk-UA",
|
fullCode: "uk-UA",
|
||||||
|
djs: () => import("dayjs/locale/uk"),
|
||||||
},
|
},
|
||||||
["el"]: {
|
["el"]: {
|
||||||
name: "Ελληνικά",
|
name: "Ελληνικά",
|
||||||
countryCode: "gr",
|
countryCode: "gr",
|
||||||
fullCode: "el-GR",
|
fullCode: "el-GR",
|
||||||
|
djs: () => import("dayjs/locale/el"),
|
||||||
},
|
},
|
||||||
["da"]: {
|
["da"]: {
|
||||||
name: "Dansk",
|
name: "Dansk",
|
||||||
countryCode: "dk",
|
countryCode: "dk",
|
||||||
fullCode: "da-DK",
|
fullCode: "da-DK",
|
||||||
|
djs: () => import("dayjs/locale/da"),
|
||||||
},
|
},
|
||||||
["pt"]: {
|
["pt"]: {
|
||||||
name: "Português",
|
name: "Português",
|
||||||
countryCode: "pt",
|
countryCode: "pt",
|
||||||
fullCode: "pt-PT",
|
fullCode: "pt-PT",
|
||||||
|
djs: () => import("dayjs/locale/pt"),
|
||||||
},
|
},
|
||||||
["fa"]: {
|
["fa"]: {
|
||||||
name: "فارسی",
|
name: "فارسی",
|
||||||
countryCode: "ir",
|
countryCode: "ir",
|
||||||
fullCode: "fa-IR",
|
fullCode: "fa-IR",
|
||||||
|
djs: () => import("dayjs/locale/fa"),
|
||||||
},
|
},
|
||||||
["ro"]: {
|
["ro"]: {
|
||||||
name: "Român",
|
name: "Român",
|
||||||
countryCode: "ro",
|
countryCode: "ro",
|
||||||
fullCode: "ro-RO",
|
fullCode: "ro-RO",
|
||||||
|
djs: () => import("dayjs/locale/ro"),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,4 +157,8 @@ i18n
|
|||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
i18n.on("languageChanged", function (lng) {
|
||||||
|
languages[lng].djs().then((djs) => dayjs.locale(djs.name));
|
||||||
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function LocationContainer() {
|
|||||||
|
|
||||||
const grouped: Record<string, ISpool[]> = {};
|
const grouped: Record<string, ISpool[]> = {};
|
||||||
spools.forEach((spool) => {
|
spools.forEach((spool) => {
|
||||||
const loc = spool.location ?? t("spool.no_location");
|
const loc = spool.location ?? t("locations.no_location");
|
||||||
if (!grouped[loc]) {
|
if (!grouped[loc]) {
|
||||||
grouped[loc] = [];
|
grouped[loc] = [];
|
||||||
}
|
}
|
||||||
@@ -155,12 +155,13 @@ export function LocationContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addNewLocation = () => {
|
const addNewLocation = () => {
|
||||||
let newLocationName = "New Location";
|
const baseLocationName = t("locations.new_location");
|
||||||
|
let newLocationName = baseLocationName;
|
||||||
|
|
||||||
const newLocs = [...locationsList];
|
const newLocs = [...locationsList];
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (newLocs.includes(newLocationName)) {
|
while (newLocs.includes(newLocationName)) {
|
||||||
newLocationName = "New Location " + i;
|
newLocationName = baseLocationName + " " + i;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
newLocs.push(newLocationName);
|
newLocs.push(newLocationName);
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ import type { Identifier, XYCoord } from "dnd-core";
|
|||||||
import { useDrag, useDrop } from "react-dnd";
|
import { useDrag, useDrop } from "react-dnd";
|
||||||
|
|
||||||
import { EditOutlined, EyeOutlined } from "@ant-design/icons";
|
import { EditOutlined, EyeOutlined } from "@ant-design/icons";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
|
import utc from "dayjs/plugin/utc";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import SpoolIcon from "../../../components/spoolIcon";
|
import SpoolIcon from "../../../components/spoolIcon";
|
||||||
import { ISpool } from "../../spools/model";
|
import { ISpool } from "../../spools/model";
|
||||||
import { ItemTypes, SpoolDragItem, useCurrentDraggedSpool } from "../dnd";
|
import { ItemTypes, SpoolDragItem, useCurrentDraggedSpool } from "../dnd";
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
const { useToken } = theme;
|
const { useToken } = theme;
|
||||||
|
|
||||||
export function SpoolCard({
|
export function SpoolCard({
|
||||||
@@ -134,6 +140,21 @@ export function SpoolCard({
|
|||||||
};
|
};
|
||||||
drag(drop(ref));
|
drag(drop(ref));
|
||||||
|
|
||||||
|
function formatSubtitle(spool: ISpool) {
|
||||||
|
let str = "";
|
||||||
|
if (spool.filament.material) str += spool.filament.material + " - ";
|
||||||
|
if (spool.filament.weight) {
|
||||||
|
const remaining_weight = spool.remaining_weight ?? spool.filament.weight;
|
||||||
|
str += `${remaining_weight} / ${spool.filament.weight} g`;
|
||||||
|
}
|
||||||
|
if (spool.last_used) {
|
||||||
|
// Format like "last used X time ago"
|
||||||
|
const dt = dayjs(spool.last_used);
|
||||||
|
str += ` - ${t("spool.formats.last_used", { date: dt.fromNow() })}`;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="spool" ref={ref} style={style} data-handler-id={handlerId}>
|
<div className="spool" ref={ref} style={style} data-handler-id={handlerId}>
|
||||||
<SpoolIcon color={colorObj} />
|
<SpoolIcon color={colorObj} />
|
||||||
@@ -157,7 +178,7 @@ export function SpoolCard({
|
|||||||
color: token.colorTextSecondary,
|
color: token.colorTextSecondary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{spool.remaining_weight} / {spool.filament.weight} g
|
{formatSubtitle(spool)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user