mirror of https://github.com/usememos/memos
feat: improve i18n support as a whole (#1526)
* feat: improve i18n support as a whole
- Remove dayjs in favor of /helpers/datetime.ts, which uses
Intl.DateTimeFormat and Date. Dayjs is not exactly i18n friendly
and has several locale related opened issues.
- Move/refactor date/time code from /helpers/utils.ts to
/helpers/datetime.ts.
- Fix Daily Review weekday not changing according to selected date.
- Localize Daily review weekday and month.
- Load i18n listed strings from /locales/{locale}.json in a dynamic way.
This makes much easier to add new locales, by just adding a properly
named json file and listing it only in /web/src/i18n.ts and
/api/user_setting.go.
- Fallback languages are now set in /web/src/i18n.ts.
- Full language codes are now preffered, but they fallback to 2-letter
codes when not available.
- The locale dropdown is now populated dynamically from the available
locales. Locale names are populated by the browser via
Intl.DisplayNames(locale).
- /web/src/i18n.ts now exports a type TLocale from availableLocales
array. This is used only by findNearestLanguageMatch(). As I was unable
to use this type in ".d.ts" files, I switched the Locale type from
/web/src/types/i18n.d.ts to string.
- Move pretty much all hardcoded text strings to i18n strings.
- Add pt-BR translation.
- Remove site.ts and move its content to a i18n string.
- Rename zh.json to zh-Hans.json to get the correct language name on
selector dropdown.
- Remove pt_BR.json and replace with pt-BR.json.
- Some minor layout spacing fixes to accommodate larger texts.
- Improve some error messages.
* Delete .yarnrc.yml
* Delete package-lock.json
* fix: 158:28 error Insert `⏎` prettier/prettier
pull/1530/head
parent
5652bb76d4
commit
557278fac0
@ -0,0 +1,207 @@
|
|||||||
|
import i18n from "@/i18n";
|
||||||
|
|
||||||
|
export function convertToMillis(localSetting: LocalSetting) {
|
||||||
|
const hoursToMillis = localSetting.dailyReviewTimeOffset * 60 * 60 * 1000;
|
||||||
|
return hoursToMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNowTimeStamp(): number {
|
||||||
|
return Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTimeStampByDate(t: Date | number | string): number {
|
||||||
|
if (typeof t === "string") {
|
||||||
|
t = t.replaceAll("-", "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(t).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDateStampByDate(t?: Date | number | string): number {
|
||||||
|
const tsFromDate = getTimeStampByDate(t ? t : Date.now());
|
||||||
|
const d = new Date(tsFromDate);
|
||||||
|
|
||||||
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNormalizedDateString(t?: Date | number | string): string {
|
||||||
|
const tsFromDate = getTimeStampByDate(t ? t : Date.now());
|
||||||
|
const d = new Date(tsFromDate);
|
||||||
|
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = d.getMonth() + 1;
|
||||||
|
const date = d.getDate();
|
||||||
|
|
||||||
|
return `${year}/${month}/${date}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a time string to provided time.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
*
|
||||||
|
* Output is always ``HH:MM`` (24-hour format)
|
||||||
|
*/
|
||||||
|
export function getTimeString(t?: Date | number | string): string {
|
||||||
|
const tsFromDate = getTimeStampByDate(t ? t : Date.now());
|
||||||
|
const d = new Date(tsFromDate);
|
||||||
|
|
||||||
|
const hours = d.getHours();
|
||||||
|
const mins = d.getMinutes();
|
||||||
|
|
||||||
|
const hoursStr = hours < 10 ? "0" + hours : hours;
|
||||||
|
const minsStr = mins < 10 ? "0" + mins : mins;
|
||||||
|
|
||||||
|
return `${hoursStr}:${minsStr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a localized date and time string to provided time.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
*
|
||||||
|
* Sample outputs:
|
||||||
|
* - "en" locale: "1/30/2023, 10:05:00 PM"
|
||||||
|
* - "pt-BR" locale: "30/01/2023 22:05:00"
|
||||||
|
* - "pl" locale: "30.01.2023, 22:05:00"
|
||||||
|
*/
|
||||||
|
export function getDateTimeString(t?: Date | number | string, locale = i18n.language): string {
|
||||||
|
const tsFromDate = getTimeStampByDate(t ? t : Date.now());
|
||||||
|
|
||||||
|
return new Date(tsFromDate).toLocaleDateString(locale, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a localized date string to provided time.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
*
|
||||||
|
* Note: This function does not include time information.
|
||||||
|
*
|
||||||
|
* Sample outputs:
|
||||||
|
* - "en" locale: "1/30/2023"
|
||||||
|
* - "pt-BR" locale: "30/01/2023"
|
||||||
|
* - "pl" locale: "30.01.2023"
|
||||||
|
*/
|
||||||
|
export function getDateString(t?: Date | number | string, locale = i18n.language): string {
|
||||||
|
const tsFromDate = getTimeStampByDate(t ? t : Date.now());
|
||||||
|
|
||||||
|
return new Date(tsFromDate).toLocaleDateString(locale, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a localized relative time string to provided time.
|
||||||
|
*
|
||||||
|
* Possible outputs for "long" format and "en" locale:
|
||||||
|
* - "x seconds ago"
|
||||||
|
* - "x minutes ago"
|
||||||
|
* - "x hours ago"
|
||||||
|
* - "yesterday"
|
||||||
|
* - "x days ago"
|
||||||
|
* - "x weeks ago"
|
||||||
|
* - "x months ago"
|
||||||
|
* - "last year"
|
||||||
|
* - "x years ago"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const getRelativeTimeString = (time: number, locale = i18n.language, formatStyle: "long" | "short" | "narrow" = "long"): string => {
|
||||||
|
const pastTimeMillis = Date.now() - time;
|
||||||
|
const secMillis = 1000;
|
||||||
|
const minMillis = secMillis * 60;
|
||||||
|
const hourMillis = minMillis * 60;
|
||||||
|
const dayMillis = hourMillis * 24;
|
||||||
|
|
||||||
|
// numeric: "auto" provides "yesterday" for 1 day ago, "always" provides "1 day ago"
|
||||||
|
const formatOpts = { style: formatStyle, numeric: "auto" } as Intl.RelativeTimeFormatOptions;
|
||||||
|
|
||||||
|
const relTime = new Intl.RelativeTimeFormat(locale, formatOpts);
|
||||||
|
|
||||||
|
if (pastTimeMillis < minMillis) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / secMillis), "second");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pastTimeMillis < hourMillis) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / minMillis), "minute");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pastTimeMillis < dayMillis) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / hourMillis), "hour");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pastTimeMillis < dayMillis * 7) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / dayMillis), "day");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pastTimeMillis < dayMillis * 30) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 7)), "week");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pastTimeMillis < dayMillis * 365) {
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 30)), "month");
|
||||||
|
}
|
||||||
|
|
||||||
|
return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 365)), "year");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the normalized date string of the provided date.
|
||||||
|
* Format is always `YYYY-MM-DDT00:00`.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
*/
|
||||||
|
export function getNormalizedTimeString(t?: Date | number | string): string {
|
||||||
|
const date = new Date(t ? t : Date.now());
|
||||||
|
|
||||||
|
const yyyy = date.getFullYear();
|
||||||
|
const M = date.getMonth() + 1;
|
||||||
|
const d = date.getDate();
|
||||||
|
const h = date.getHours();
|
||||||
|
const m = date.getMinutes();
|
||||||
|
|
||||||
|
const MM = M < 10 ? "0" + M : M;
|
||||||
|
const dd = d < 10 ? "0" + d : d;
|
||||||
|
const hh = h < 10 ? "0" + h : h;
|
||||||
|
const mm = m < 10 ? "0" + m : m;
|
||||||
|
|
||||||
|
return `${yyyy}-${MM}-${dd}T${hh}:${mm}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the number of **milliseconds** since the Unix Epoch of the provided date.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* getUnixTimeMillis("2019-01-25 00:00") // 1548381600000
|
||||||
|
* ```
|
||||||
|
* To get a Unix timestamp (the number of seconds since the epoch), use `getUnixTime()`.
|
||||||
|
*/
|
||||||
|
export function getUnixTimeMillis(t?: Date | number | string): number {
|
||||||
|
const date = new Date(t ? t : Date.now());
|
||||||
|
return date.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the Unix timestamp (the number of **seconds** since the Unix Epoch) of the provided date.
|
||||||
|
*
|
||||||
|
* If no date is provided, the current date is used.
|
||||||
|
* ```
|
||||||
|
* getUnixTime("2019-01-25 00:00") // 1548381600
|
||||||
|
* ```
|
||||||
|
* This value is floored to the nearest second, and does not include a milliseconds component.
|
||||||
|
*/
|
||||||
|
export function getUnixTime(t?: Date | number | string): number {
|
||||||
|
const date = new Date(t ? t : Date.now());
|
||||||
|
return Math.floor(date.getTime() / 1000);
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
export const slogan = "A lightweight, self-hosted memo hub. Open Source and Free forever.";
|
|
||||||
@ -1,79 +1,57 @@
|
|||||||
import i18n from "i18next";
|
import i18n, { FallbackLng, FallbackLngObjList } from "i18next";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
import enLocale from "./locales/en.json";
|
import toast from "react-hot-toast";
|
||||||
import zhLocale from "./locales/zh.json";
|
|
||||||
import viLocale from "./locales/vi.json";
|
|
||||||
import frLocale from "./locales/fr.json";
|
|
||||||
import nlLocale from "./locales/nl.json";
|
|
||||||
import svLocale from "./locales/sv.json";
|
|
||||||
import deLocale from "./locales/de.json";
|
|
||||||
import esLocale from "./locales/es.json";
|
|
||||||
import ukLocale from "./locales/uk.json";
|
|
||||||
import ruLocale from "./locales/ru.json";
|
|
||||||
import itLocale from "./locales/it.json";
|
|
||||||
import hantLocale from "./locales/zh-Hant.json";
|
|
||||||
import trLocale from "./locales/tr.json";
|
|
||||||
import koLocale from "./locales/ko.json";
|
|
||||||
import slLocale from "./locales/sl.json";
|
|
||||||
|
|
||||||
const DETECTION_OPTIONS = {
|
// eslint-disable-next-line prettier/prettier
|
||||||
order: ["navigator"],
|
export const availableLocales = [
|
||||||
};
|
"de",
|
||||||
|
"en",
|
||||||
|
"es",
|
||||||
|
"fr",
|
||||||
|
"it",
|
||||||
|
"ko",
|
||||||
|
"nl",
|
||||||
|
"pl",
|
||||||
|
"pt-BR",
|
||||||
|
"ru",
|
||||||
|
"sl",
|
||||||
|
"sv",
|
||||||
|
"tr",
|
||||||
|
"uk",
|
||||||
|
"vi",
|
||||||
|
"zh-Hans",
|
||||||
|
"zh-Hant",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const fallbacks = {
|
||||||
|
"zh-HK": ["zh-Hant", "en"],
|
||||||
|
"zh-TW": ["zh-Hant", "en"],
|
||||||
|
zh: ["zh-Hans", "en"],
|
||||||
|
} as FallbackLngObjList;
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
detection: DETECTION_OPTIONS,
|
detection: {
|
||||||
resources: {
|
order: ["navigator"],
|
||||||
en: {
|
|
||||||
translation: enLocale,
|
|
||||||
},
|
|
||||||
zh: {
|
|
||||||
translation: zhLocale,
|
|
||||||
},
|
|
||||||
vi: {
|
|
||||||
translation: viLocale,
|
|
||||||
},
|
|
||||||
fr: {
|
|
||||||
translation: frLocale,
|
|
||||||
},
|
|
||||||
nl: {
|
|
||||||
translation: nlLocale,
|
|
||||||
},
|
|
||||||
sv: {
|
|
||||||
translation: svLocale,
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
translation: deLocale,
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
translation: esLocale,
|
|
||||||
},
|
|
||||||
uk: {
|
|
||||||
translation: ukLocale,
|
|
||||||
},
|
|
||||||
ru: {
|
|
||||||
translation: ruLocale,
|
|
||||||
},
|
|
||||||
it: {
|
|
||||||
translation: itLocale,
|
|
||||||
},
|
|
||||||
hant: {
|
|
||||||
translation: hantLocale,
|
|
||||||
},
|
|
||||||
tr: {
|
|
||||||
translation: trLocale,
|
|
||||||
},
|
|
||||||
ko: {
|
|
||||||
translation: koLocale,
|
|
||||||
},
|
|
||||||
sl: {
|
|
||||||
translation: slLocale,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
fallbackLng: "en",
|
fallbackLng: {
|
||||||
|
...fallbacks,
|
||||||
|
...{ default: ["en"] },
|
||||||
|
} as FallbackLng,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const locale of availableLocales) {
|
||||||
|
import(`./locales/${locale}.json`)
|
||||||
|
.then((translation) => {
|
||||||
|
i18n.addResourceBundle(locale, "translation", translation.default, true, true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(`Failed to load locale "${locale}".\n${err}`, { duration: 5000 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
export type TLocale = typeof availableLocales[number];
|
||||||
|
|||||||
@ -0,0 +1,389 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"memos-slogan": "Uma central de anotações leve e auto-hospedada. De código aberto e gratuito para sempre.",
|
||||||
|
"about": "Sobre",
|
||||||
|
"home": "Início",
|
||||||
|
"resources": "Recursos",
|
||||||
|
"settings": "Configurações",
|
||||||
|
"archived": "Arquivado",
|
||||||
|
"email": "Email",
|
||||||
|
"password": "Senha",
|
||||||
|
"avatar": "Avatar",
|
||||||
|
"username": "Nome de usuário",
|
||||||
|
"nickname": "Apelido",
|
||||||
|
"save": "Salvar",
|
||||||
|
"close": "Fechar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"create": "Criar",
|
||||||
|
"update": "Atualizar",
|
||||||
|
"change": "Alterar",
|
||||||
|
"confirm": "Confirmar",
|
||||||
|
"reset": "Redefinir",
|
||||||
|
"language": "Idioma",
|
||||||
|
"version": "Versão",
|
||||||
|
"pin": "Fixar",
|
||||||
|
"unpin": "Desafixar",
|
||||||
|
"edit": "Editar",
|
||||||
|
"restore": "Restaurar",
|
||||||
|
"delete": "Deletar",
|
||||||
|
"null": "Nulo",
|
||||||
|
"share": "Compartilhar",
|
||||||
|
"archive": "Arquivar",
|
||||||
|
"basic": "Básico",
|
||||||
|
"admin": "Admin",
|
||||||
|
"explore": "Explorar",
|
||||||
|
"sign-in": "Entrar",
|
||||||
|
"sign-in-with": "Entrar usando {{provider}}",
|
||||||
|
"or": "ou",
|
||||||
|
"sign-up": "Registrar",
|
||||||
|
"sign-out": "Sair",
|
||||||
|
"type": "Tipo",
|
||||||
|
"shortcuts": "Atalhos",
|
||||||
|
"title": "Título",
|
||||||
|
"filter": "Filtro",
|
||||||
|
"filter-period": "{{from}} até {{to}}",
|
||||||
|
"tags": "Tags",
|
||||||
|
"yourself": "Você mesmo",
|
||||||
|
"changed": "alterado",
|
||||||
|
"fold": "Recolher",
|
||||||
|
"expand": "Expandir",
|
||||||
|
"image": "Imagem",
|
||||||
|
"link": "Link",
|
||||||
|
"vacuum": "Compactar (vacuum)",
|
||||||
|
"select": "Selecionar",
|
||||||
|
"database": "Banco de dados",
|
||||||
|
"upload": "Carregar",
|
||||||
|
"preview": "Pré-visualizar",
|
||||||
|
"rename": "Renomear",
|
||||||
|
"clear": "Limpar",
|
||||||
|
"name": "Nome",
|
||||||
|
"visibility": "Visibilidade",
|
||||||
|
"learn-more": "Saiba mais",
|
||||||
|
"e.g": "ex.",
|
||||||
|
"beta": "Beta"
|
||||||
|
},
|
||||||
|
"router": {
|
||||||
|
"back-to-home": "Voltar ao início"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"signup-as-host": "Registrar como Host",
|
||||||
|
"host-tip": "Você está se registrando como Host do Site.",
|
||||||
|
"not-host-tip": "Se você não tem uma conta, por favor, entre em contato com o Host do site.",
|
||||||
|
"new-password": "Nova senha",
|
||||||
|
"repeat-new-password": "Repita a nova senha"
|
||||||
|
},
|
||||||
|
"editor": {
|
||||||
|
"editing": "Editando...",
|
||||||
|
"cancel-edit": "Cancelar edição",
|
||||||
|
"save": "Salvar",
|
||||||
|
"placeholder": "Alguma ideia...",
|
||||||
|
"only-image-supported": "Apenas imagens são suportadas",
|
||||||
|
"cant-empty": "O conteúdo não pode estar vazio",
|
||||||
|
"local": "Local",
|
||||||
|
"resources": "Recursos"
|
||||||
|
},
|
||||||
|
"memo": {
|
||||||
|
"view-detail": "Ver detalhes",
|
||||||
|
"copy-link": "Copiar link",
|
||||||
|
"embed": "Incorporar",
|
||||||
|
"archived-memos": "Memos arquivados",
|
||||||
|
"no-archived-memos": "Nenhum memo arquivado",
|
||||||
|
"fetching-data": "obtendo dados...",
|
||||||
|
"fetch-more": "Clique para carregar mais dados",
|
||||||
|
"archived-at": "Arquivado em",
|
||||||
|
"search-placeholder": "Pesquisar memos",
|
||||||
|
"visibility": {
|
||||||
|
"private": "Privado (eu)",
|
||||||
|
"protected": "Protegido (membros)",
|
||||||
|
"public": "Público (todos)",
|
||||||
|
"disabled": "Memos públicos estão desabilitados"
|
||||||
|
},
|
||||||
|
"delete-memo": "Deletar memo",
|
||||||
|
"delete-confirm": "Tem certeza de que deseja deletar este memo?\n\nESTA AÇÃO É IRREVERSÍVEL❗"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"no-resources": "Nenhum recurso",
|
||||||
|
"fetching-data": "obtendo dados...",
|
||||||
|
"copy-link": "Copiar link",
|
||||||
|
"reset-link": "Redefinir link",
|
||||||
|
"reset-resource-link": "Redefinir link do recurso",
|
||||||
|
"reset-link-prompt": "Tem certeza de que deseja redefinir o link?\nIsso quebrará todos os vínculos atuais.\n\nESTA AÇÃO É IRREVERSÍVEL❗",
|
||||||
|
"delete-resource": "Deletar recurso",
|
||||||
|
"linked-amount": "Quantidade de vínculos",
|
||||||
|
"warning-text": "Tem certeza de que deseja deletar este recurso?\n\nESTA AÇÃO É IRREVERSÍVEL❗",
|
||||||
|
"warning-text-unused": "Tem certeza de que deseja deletar este recurso não utilizado?\n\nESTA AÇÃO É IRREVERSÍVEL❗",
|
||||||
|
"no-unused-resources": "Nenhum recurso não utilizado",
|
||||||
|
"delete-selected-resources": "Deletar recursos selecionados",
|
||||||
|
"no-files-selected": "Nenhum arquivo selecionado❗",
|
||||||
|
"upload-successfully": "Carregado com êxito",
|
||||||
|
"file-drag-drop-prompt": "Arraste e solte o arquivo aqui para carregá-lo",
|
||||||
|
"search-bar-placeholder": "Pesquisar recurso",
|
||||||
|
"create-dialog": {
|
||||||
|
"title": "Criar recurso",
|
||||||
|
"upload-method": "Método de carregamento",
|
||||||
|
"local-file": {
|
||||||
|
"option": "Arquivo local",
|
||||||
|
"choose": "Escolher arquivo"
|
||||||
|
},
|
||||||
|
"external-link": {
|
||||||
|
"option": "Link externo",
|
||||||
|
"link": "Link",
|
||||||
|
"link-placeholder": "https://o.link.para/seu/recurso",
|
||||||
|
"file-name": "Nome",
|
||||||
|
"file-name-placeholder": "Nome do arquivo",
|
||||||
|
"type": "Tipo",
|
||||||
|
"type-placeholder": "Tipo do arquivo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shortcut-list": {
|
||||||
|
"shortcut-title": "título do atalho",
|
||||||
|
"create-shortcut": "Criar atalho",
|
||||||
|
"edit-shortcut": "Editar atalho",
|
||||||
|
"eligible-memo": "memo elegível",
|
||||||
|
"fill-previous": "Por favor, preencha o valor do filtro anterior",
|
||||||
|
"title-required": "O título do atalho é obrigatório",
|
||||||
|
"value-required": "O valor do filtro é obrigatório"
|
||||||
|
},
|
||||||
|
"tag-list": {
|
||||||
|
"tip-text": "Insira `#tag` para criar uma nova",
|
||||||
|
"create-tag": "Criar Tag",
|
||||||
|
"all-tags": "Todas as Tags",
|
||||||
|
"tag-name": "NOME_DA_TAG"
|
||||||
|
},
|
||||||
|
"daily-review": {
|
||||||
|
"title": "Resumo Diário",
|
||||||
|
"no-memos": "Não há memos"
|
||||||
|
},
|
||||||
|
"setting": {
|
||||||
|
"my-account": "Minha conta",
|
||||||
|
"preference": "Preferências",
|
||||||
|
"member": "Membro",
|
||||||
|
"member-list": "Lista de membros",
|
||||||
|
"system": "Sistema",
|
||||||
|
"storage": "Armazenamento",
|
||||||
|
"sso": "SSO",
|
||||||
|
"account-section": {
|
||||||
|
"title": "Informação da conta",
|
||||||
|
"username-note": "Usado para entrar",
|
||||||
|
"nickname-note": "Exibido no banner",
|
||||||
|
"email-note": "Opcional",
|
||||||
|
"update-information": "Atualizar informações",
|
||||||
|
"change-password": "Alterar senha",
|
||||||
|
"reset-api": "Redefinir API",
|
||||||
|
"openapi-title": "OpenAPI",
|
||||||
|
"openapi-reset": "Redefinir chave OpenAPI",
|
||||||
|
"openapi-reset-warning": "❗ A chave de API existente será invalidada e uma nova será gerada.\n\nTem certeza de que deseja redefinir?",
|
||||||
|
"openapi-sample-post": "Olá #memos em {{url}}"
|
||||||
|
},
|
||||||
|
"preference-section": {
|
||||||
|
"theme": "Tema",
|
||||||
|
"default-memo-visibility": "Visibilidade padrão do memo",
|
||||||
|
"default-resource-visibility": "Visibilidade padrão do recurso",
|
||||||
|
"enable-folding-memo": "Habilitar recolher memo",
|
||||||
|
"enable-double-click": "Habilitar duplo clique para editar",
|
||||||
|
"editor-font-style": "Estilo de fonte do editor",
|
||||||
|
"mobile-editor-style": "Estilo do editor para dispositivos móveis",
|
||||||
|
"default-memo-sort-option": "Memo display time",
|
||||||
|
"created_ts": "Hora de criação",
|
||||||
|
"updated_ts": "Hora de atualização",
|
||||||
|
"daily-review-time-offset": "Compensação de tempo do Resumo Diário"
|
||||||
|
},
|
||||||
|
"storage-section": {
|
||||||
|
"current-storage": "Armazenamento atual",
|
||||||
|
"type-database": "Banco de dados",
|
||||||
|
"type-local": "Local",
|
||||||
|
"storage-services-list": "Lista de serviços de armazenamento",
|
||||||
|
"create-a-service": "Criar um serviço",
|
||||||
|
"update-a-service": "Atualizar um serviço",
|
||||||
|
"warning-text": "Tem certeza de que deseja deletar o serviço de armazenamento \"{{name}}\"?\n\nESTA AÇÃO É IRREVERSÍVEL❗",
|
||||||
|
"delete-storage": "Deletar armazenamento",
|
||||||
|
"local-storage-path": "Caminho do armazenamento local",
|
||||||
|
"update-local-path": "Atualizar caminho do armazenamento local",
|
||||||
|
"update-local-path-description": "O caminho de armazenamento local é relativo ao seu banco de dados",
|
||||||
|
"create-storage": "Criar armazenamento",
|
||||||
|
"update-storage": "Atualizar armazenamento",
|
||||||
|
"endpoint": "Endpoint",
|
||||||
|
"s3-compatible-url": "URL compatível com S3",
|
||||||
|
"region": "Região",
|
||||||
|
"region-placeholder": "Nome da região",
|
||||||
|
"accesskey": "Chave de acesso",
|
||||||
|
"accesskey-placeholder": "Chave de acesso / ID de acesso",
|
||||||
|
"secretkey": "Chave secreta",
|
||||||
|
"secretkey-placeholder": "Chave secreta / Chave de acesso",
|
||||||
|
"bucket": "Bucket",
|
||||||
|
"bucket-placeholder": "Nome do bucket",
|
||||||
|
"path": "Caminho do armazenamento",
|
||||||
|
"path-description": "Você pode usar as mesmas variáveis dinâmicas do armazenamento local, como {filename}",
|
||||||
|
"path-placeholder": "caminho/personalizado",
|
||||||
|
"url-prefix": "Prefixo da URL",
|
||||||
|
"url-prefix-placeholder": "Prefixo personalizado da URL, opcional",
|
||||||
|
"url-suffix": "Sufixo da URL",
|
||||||
|
"url-suffix-placeholder": "Sufixo personalizado da URL, opcional"
|
||||||
|
},
|
||||||
|
"member-section": {
|
||||||
|
"create-a-member": "Criar um membro",
|
||||||
|
"archive-member": "Arquivar membro",
|
||||||
|
"archive-warning": "❗ Tem certeza de que deseja arquivar {{username}}?",
|
||||||
|
"delete-member": "Deletar membro",
|
||||||
|
"delete-warning": "❗ Tem certeza de que deseja deletar {{username}}?\n\nESTA AÇÃO É IRREVERSÍVEL❗"
|
||||||
|
},
|
||||||
|
"system-section": {
|
||||||
|
"server-name": "Nome do servidor",
|
||||||
|
"customize-server": {
|
||||||
|
"title": "Personalizar Servidor",
|
||||||
|
"default": "O padrão é memos",
|
||||||
|
"icon-url": "URL do ícone",
|
||||||
|
"description": "Descrição",
|
||||||
|
"locale": "Localização do servidor",
|
||||||
|
"appearance": "Aparência do servidor"
|
||||||
|
},
|
||||||
|
"database-file-size": "Tamanho do banco de dados",
|
||||||
|
"allow-user-signup": "Permitir registro de usuário",
|
||||||
|
"ignore-version-upgrade": "Ignorar atualização de versão",
|
||||||
|
"disable-public-memos": "Desabilitar memos públicos",
|
||||||
|
"additional-style": "Estilo adicional",
|
||||||
|
"additional-script": "Script adicional",
|
||||||
|
"additional-style-placeholder": "Código CSS adicional",
|
||||||
|
"additional-script-placeholder": "Código JavaScript adicional",
|
||||||
|
"openai-api-key": "OpenAI: Chave de API",
|
||||||
|
"openai-api-key-description": "Obter chave de API",
|
||||||
|
"openai-api-key-placeholder": "Sua chave de API da OpenAI",
|
||||||
|
"openai-api-host": "OpenAI: Host de API",
|
||||||
|
"openai-api-host-placeholder": "Padrão: https://api.openai.com/"
|
||||||
|
},
|
||||||
|
"appearance-option": {
|
||||||
|
"system": "Sistema",
|
||||||
|
"light": "Claro",
|
||||||
|
"dark": "Escuro"
|
||||||
|
},
|
||||||
|
"sso-section": {
|
||||||
|
"sso-list": "Lista de SSOs (Login Único)",
|
||||||
|
"delete-sso": "Confirmar exclusão",
|
||||||
|
"confirm-delete": "Tem certeza de que deseja excluir a configuração SSO \"{{name}}\"?\n\nESTA AÇÃO É IRREVERSÍVEL❗",
|
||||||
|
"create-sso": "Criar SSO",
|
||||||
|
"update-sso": "Atualizar SSO",
|
||||||
|
"sso-created": "SSO {{name}} criado",
|
||||||
|
"sso-updated": "SSO {{name}} atualizado",
|
||||||
|
"identifier": "Identificador",
|
||||||
|
"display-name": "Nome de exibição",
|
||||||
|
"template": "Modelo",
|
||||||
|
"custom": "Personalizado",
|
||||||
|
"identifier-filter": "Filtro identificador",
|
||||||
|
"redirect-url": "URL de redirecionamento",
|
||||||
|
"client-id": "ID do cliente",
|
||||||
|
"client-secret": "Segredo do cliente",
|
||||||
|
"authorization-endpoint": "Endpoint de autenticação",
|
||||||
|
"token-endpoint": "Endpoint de token",
|
||||||
|
"user-endpoint": "Endpoint do usuário",
|
||||||
|
"scopes": "Escopos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"new-filter": "Novo filtro",
|
||||||
|
"type": {
|
||||||
|
"tag": "Tag",
|
||||||
|
"type": "Tipo",
|
||||||
|
"text": "Texto",
|
||||||
|
"display-time": "Hora de exibição",
|
||||||
|
"visibility": "Visibilidade"
|
||||||
|
},
|
||||||
|
"operator": {
|
||||||
|
"contains": "Contém",
|
||||||
|
"not-contains": "Não contém",
|
||||||
|
"is": "É",
|
||||||
|
"is-not": "Não É",
|
||||||
|
"before": "Antes",
|
||||||
|
"after": "Depois"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"not-tagged": "Sem tags",
|
||||||
|
"linked": "Tem links",
|
||||||
|
"has-attachment": "Tem anexos"
|
||||||
|
},
|
||||||
|
"text-placeholder": "Inicie com ^ para usar regex",
|
||||||
|
"and": "E",
|
||||||
|
"or": "Ou"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"no-memos": "nenhum memo 🌃",
|
||||||
|
"memos-ready": "todos os memos estão prontos 🎉",
|
||||||
|
"no-resource": "nenhum recurso 🌃",
|
||||||
|
"resource-ready": "todos os recursos estão prontos 🎉",
|
||||||
|
"restored-successfully": "Restaurado com êxito",
|
||||||
|
"memo-updated-datetime": "Data do memo atualizada",
|
||||||
|
"invalid-created-datetime": "Data de criação inválida",
|
||||||
|
"change-memo-created-time": "Alterar data de criação do memo",
|
||||||
|
"change-memo-created-time-warning-1": "ESTE NÃO É UM PROCEDIMENTO RECOMENDADO.",
|
||||||
|
"change-memo-created-time-warning-2": "Esteja certo de que realmente precisa disso.",
|
||||||
|
"memo-not-found": "Memo não encontrado",
|
||||||
|
"fill-all": "Por favor, preencha todos os campos",
|
||||||
|
"password-not-match": "As senhas não coincidem",
|
||||||
|
"new-password-not-match": "As novas senhas não coincidem",
|
||||||
|
"image-load-failed": "Falha ao carregar a imagem",
|
||||||
|
"fill-form": "Por favor, preencha o formulário",
|
||||||
|
"fill-server-name": "Por favor, preencha o nome do servidor",
|
||||||
|
"login-failed": "Falha no login",
|
||||||
|
"signup-failed": "Falha no registro",
|
||||||
|
"user-not-found": "Usuário não encontrado",
|
||||||
|
"password-changed": "Senha alterada",
|
||||||
|
"private-only": "Este memo é privado",
|
||||||
|
"copied": "Copiado",
|
||||||
|
"succeed-copy-content": "Conteúdo copiado com êxito.",
|
||||||
|
"succeed-copy-code": "Código copiado com êxito.",
|
||||||
|
"succeed-copy-link": "Link copiado com êxito.",
|
||||||
|
"change-resource-filename": "Alterar nome do arquivo do recurso",
|
||||||
|
"resource-filename-updated": "Nome do arquivo do recurso atualizado",
|
||||||
|
"invalid-resource-filename": "Nome de arquivo de recurso inválido",
|
||||||
|
"click-to-save-the-image": "Clique para salvar a imagem",
|
||||||
|
"generating-the-screenshot": "Gerando a captura de tela...",
|
||||||
|
"count-selected-resources": "Total selecionado",
|
||||||
|
"too-short": "Muito curto",
|
||||||
|
"too-long": "Muito longo",
|
||||||
|
"not-allow-space": "Espaços não são permitidos",
|
||||||
|
"not-allow-chinese": "Caracteres chineses não são permitidos",
|
||||||
|
"succeed-vacuum-database": "Banco de dados compactado com êxito.",
|
||||||
|
"succeed-update-additional-style": "Folha de estilos adicional atualizada com êxito.",
|
||||||
|
"succeed-copy-resource-link": "Link do recurso copiado com êxito.",
|
||||||
|
"succeed-update-customized-profile": "Perfil personalizado com êxito.",
|
||||||
|
"succeed-update-additional-script": "Script adicional atualizado com êxito.",
|
||||||
|
"update-succeed": "Atualizado com êxito",
|
||||||
|
"page-not-found": "404 - Página não encontrada 😥"
|
||||||
|
},
|
||||||
|
"days": {
|
||||||
|
"mon": "Seg",
|
||||||
|
"tue": "Ter",
|
||||||
|
"wed": "Qua",
|
||||||
|
"thu": "Qui",
|
||||||
|
"fri": "Sex",
|
||||||
|
"sat": "Sáb",
|
||||||
|
"sun": "Dom"
|
||||||
|
},
|
||||||
|
"ask-ai": {
|
||||||
|
"title": "Pergunte a IA",
|
||||||
|
"not-enabled": "Você não configurou a chave de API da OpenAI.",
|
||||||
|
"go-to-settings": "Ir para configurações",
|
||||||
|
"placeholder": "Pergunte qualquer coisa para a IA..."
|
||||||
|
},
|
||||||
|
"embed-memo": {
|
||||||
|
"title": "Incorporar memo",
|
||||||
|
"text": "Copie o código abaixo para incorporar este memo em seu site.",
|
||||||
|
"only-public-supported": "* Somente memos públicos podem ser incorporados.",
|
||||||
|
"copy": "Copiar"
|
||||||
|
},
|
||||||
|
"heatmap": {
|
||||||
|
"memo-in": "{{amount}} memo em {{period}}",
|
||||||
|
"memos-in": "{{amount}} memos em {{period}}",
|
||||||
|
"memo-on": "{{amount}} memo em {{date}}",
|
||||||
|
"memos-on": "{{amount}} memos em {{date}}",
|
||||||
|
"day": "dia",
|
||||||
|
"days": "dias"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"about-memos": "Sobre o Memos",
|
||||||
|
"memos-description": "Memos é um aplicativo de bloco de notas baseado na web que você pode usar para escrever, organizar e compartilhar anotações.",
|
||||||
|
"no-server-description": "Nenhuma descrição configurada para este servidor.",
|
||||||
|
"powered-by": "Provido por",
|
||||||
|
"other-projects": "Outros projetos"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,253 +0,0 @@
|
|||||||
{
|
|
||||||
"common": {
|
|
||||||
"about": "Sobre",
|
|
||||||
"email": "E-mail",
|
|
||||||
"password": "Senha",
|
|
||||||
"repeat-password-short": "Repita",
|
|
||||||
"repeat-password": "Repita a senha",
|
|
||||||
"new-password": "Nova senha",
|
|
||||||
"repeat-new-password": "Repita a nova senha",
|
|
||||||
"avatar": "Avatar",
|
|
||||||
"username": "Nome de usuário",
|
|
||||||
"nickname": "Apelido",
|
|
||||||
"save": "Salvar",
|
|
||||||
"close": "Fechar",
|
|
||||||
"cancel": "Cancelar",
|
|
||||||
"create": "Criar",
|
|
||||||
"change": "Alterar",
|
|
||||||
"confirm": "Confirmar",
|
|
||||||
"reset": "Redefinir",
|
|
||||||
"language": "Idioma",
|
|
||||||
"version": "Versão",
|
|
||||||
"pin": "Fixar",
|
|
||||||
"unpin": "Desfixar",
|
|
||||||
"edit": "Editar",
|
|
||||||
"restore": "Restaurar",
|
|
||||||
"delete": "Apagar",
|
|
||||||
"null": "Nulo",
|
|
||||||
"share": "Compartilhar",
|
|
||||||
"archive": "Arquivar",
|
|
||||||
"basic": "Básico",
|
|
||||||
"admin": "Admin",
|
|
||||||
"explore": "Explorar",
|
|
||||||
"sign-in": "Entrar",
|
|
||||||
"sign-up": "Cadastrar-se",
|
|
||||||
"sign-out": "Deslogar",
|
|
||||||
"back-to-home": "Voltar para Início",
|
|
||||||
"type": "Type",
|
|
||||||
"shortcuts": "Atalhos",
|
|
||||||
"title": "Título",
|
|
||||||
"filter": "Filtro",
|
|
||||||
"tags": "Tags",
|
|
||||||
"yourself": "Você mesmo",
|
|
||||||
"archived-at": "Arquivado em",
|
|
||||||
"changed": "alterado",
|
|
||||||
"update-on": "Update on",
|
|
||||||
"fold": "Fold",
|
|
||||||
"expand": "Expandir",
|
|
||||||
"image": "Imagem",
|
|
||||||
"link": "Link",
|
|
||||||
"vacuum": "Vacuum",
|
|
||||||
"select": "Selecionar",
|
|
||||||
"database": "Banco de dados"
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"signup-as-host": "Sign up as Host",
|
|
||||||
"host-tip": "You are registering as the Site Host.",
|
|
||||||
"not-host-tip": "If you don't have an account, please contact the site host."
|
|
||||||
},
|
|
||||||
"sidebar": {
|
|
||||||
"daily-review": "Revisão diária",
|
|
||||||
"resources": "Recursos",
|
|
||||||
"setting": "Configurações",
|
|
||||||
"archived": "Arquivado"
|
|
||||||
},
|
|
||||||
"resource": {
|
|
||||||
"description": "View your static resources in memos. e.g. images",
|
|
||||||
"no-resources": "No resources.",
|
|
||||||
"fetching-data": "fetching data...",
|
|
||||||
"upload": "Enviar",
|
|
||||||
"preview": "Pré-visualizar",
|
|
||||||
"warning-text": "Are you sure to delete this resource? THIS ACTION IS IRREVERSIBLE❗",
|
|
||||||
"copy-link": "Copiar Link",
|
|
||||||
"delete-resource": "Delete Resource",
|
|
||||||
"linked-amount": "Linked memo amount",
|
|
||||||
"rename": "Renomear",
|
|
||||||
"clear": "Limpar",
|
|
||||||
"warning-text-unused": "Are you sure to delete these unused resource? THIS ACTION IS IRREVERSIBLE❗",
|
|
||||||
"no-unused-resources": "No unused resources",
|
|
||||||
"name": "Nome"
|
|
||||||
},
|
|
||||||
"archived": {
|
|
||||||
"archived-memos": "Memos Arquivados",
|
|
||||||
"no-archived-memos": "No archived memos.",
|
|
||||||
"fetching-data": "fetching data..."
|
|
||||||
},
|
|
||||||
"editor": {
|
|
||||||
"editing": "Editando...",
|
|
||||||
"cancel-edit": "Cancelar edição",
|
|
||||||
"save": "Salvar",
|
|
||||||
"placeholder": "Any thoughts...",
|
|
||||||
"only-image-supported": "Only image file supported.",
|
|
||||||
"cant-empty": "Content can't be empty",
|
|
||||||
"local": "Local",
|
|
||||||
"resources": "Resources"
|
|
||||||
},
|
|
||||||
"memo": {
|
|
||||||
"view-detail": "View Detail",
|
|
||||||
"copy": "Copy",
|
|
||||||
"copy-link": "Copiar Link",
|
|
||||||
"visibility": {
|
|
||||||
"private": "Visível apenas para você",
|
|
||||||
"protected": "Visível para membros",
|
|
||||||
"public": "Todos podem ver",
|
|
||||||
"disabled": "Memos públicos estão desativados"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"memo-list": {
|
|
||||||
"fetching-data": "fetching data...",
|
|
||||||
"fetch-more": "Click here to fetch more"
|
|
||||||
},
|
|
||||||
"shortcut-list": {
|
|
||||||
"shortcut-title": "shortcut title",
|
|
||||||
"create-shortcut": "Criar Atalho",
|
|
||||||
"edit-shortcut": "Editar Atalho",
|
|
||||||
"eligible-memo": "eligible memo",
|
|
||||||
"fill-previous": "Please fill in previous filter value",
|
|
||||||
"title-required": "Title is required",
|
|
||||||
"value-required": "Filter value is required"
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"new-filter": "New Filter",
|
|
||||||
"type": {
|
|
||||||
"tag": "Tag",
|
|
||||||
"type": "Type",
|
|
||||||
"text": "Texto",
|
|
||||||
"display-time": "Display Time",
|
|
||||||
"visibility": "Visibility"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"contains": "Contains",
|
|
||||||
"not-contains": "Does not contain",
|
|
||||||
"is": "Is",
|
|
||||||
"is-not": "Is Not",
|
|
||||||
"before": "Before",
|
|
||||||
"after": "After"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"not-tagged": "No tags",
|
|
||||||
"linked": "Has links"
|
|
||||||
},
|
|
||||||
"text-placeholder": "Starts with ^ to use regex"
|
|
||||||
},
|
|
||||||
"tag-list": {
|
|
||||||
"tip-text": "Input `#tag` to create"
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"quickly-filter": "Quickly filter"
|
|
||||||
},
|
|
||||||
"setting": {
|
|
||||||
"my-account": "My Account",
|
|
||||||
"preference": "Preference",
|
|
||||||
"member": "Member",
|
|
||||||
"member-list": "Member list",
|
|
||||||
"system": "System",
|
|
||||||
"storage": "Storage",
|
|
||||||
"sso": "SSO",
|
|
||||||
"account-section": {
|
|
||||||
"title": "Account Information",
|
|
||||||
"update-information": "Update Information",
|
|
||||||
"change-password": "Change password"
|
|
||||||
},
|
|
||||||
"preference-section": {
|
|
||||||
"theme": "Theme",
|
|
||||||
"default-memo-visibility": "Default memo visibility",
|
|
||||||
"enable-folding-memo": "Enable folding memo",
|
|
||||||
"enable-double-click": "Enable double-click to edit",
|
|
||||||
"editor-font-style": "Editor font style",
|
|
||||||
"mobile-editor-style": "Mobile editor style",
|
|
||||||
"default-memo-sort-option": "Memo display time",
|
|
||||||
"created_ts": "Created Time",
|
|
||||||
"updated_ts": "Updated Time"
|
|
||||||
},
|
|
||||||
"storage-section": {
|
|
||||||
"storage-services-list": "Storage service list",
|
|
||||||
"create-a-service": "Create a service",
|
|
||||||
"update-a-service": "Update a service",
|
|
||||||
"warning-text": "Are you sure to delete this storage service? THIS ACTION IS IRREVERSIBLE❗",
|
|
||||||
"delete-storage": "Delete Storage"
|
|
||||||
},
|
|
||||||
"member-section": {
|
|
||||||
"create-a-member": "Create a member"
|
|
||||||
},
|
|
||||||
"system-section": {
|
|
||||||
"server-name": "Server Name",
|
|
||||||
"customize-server": {
|
|
||||||
"title": "Customize Server",
|
|
||||||
"default": "Default is memos",
|
|
||||||
"icon-url": "Icon URL"
|
|
||||||
},
|
|
||||||
"database-file-size": "Database File Size",
|
|
||||||
"allow-user-signup": "Allow user signup",
|
|
||||||
"disable-public-memos": "Disable public memos",
|
|
||||||
"additional-style": "Additional style",
|
|
||||||
"additional-script": "Additional script",
|
|
||||||
"additional-style-placeholder": "Additional CSS codes",
|
|
||||||
"additional-script-placeholder": "Additional JavaScript codes"
|
|
||||||
},
|
|
||||||
"appearance-option": {
|
|
||||||
"system": "Follow system",
|
|
||||||
"light": "Always light",
|
|
||||||
"dark": "Always dark"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"no-memos": "no memos 🌃",
|
|
||||||
"memos-ready": "all memos are ready 🎉",
|
|
||||||
"restored-successfully": "Restored successfully",
|
|
||||||
"memo-updated-datetime": "Memo created datetime changed.",
|
|
||||||
"invalid-created-datetime": "Invalid created datetime.",
|
|
||||||
"change-memo-created-time": "Change memo created time",
|
|
||||||
"memo-not-found": "Memo not found.",
|
|
||||||
"fill-all": "Please fill in all fields.",
|
|
||||||
"password-not-match": "Passwords do not match.",
|
|
||||||
"new-password-not-match": "New passwords do not match.",
|
|
||||||
"image-load-failed": "Image load failed",
|
|
||||||
"fill-form": "Please fill out this form",
|
|
||||||
"login-failed": "Login failed",
|
|
||||||
"signup-failed": "Signup failed",
|
|
||||||
"user-not-found": "User not found",
|
|
||||||
"password-changed": "Password Changed",
|
|
||||||
"private-only": "This memo is private only.",
|
|
||||||
"copied": "Copied",
|
|
||||||
"succeed-copy-content": "Succeed to copy content to clipboard.",
|
|
||||||
"succeed-copy-code": "Succeed to copy code to clipboard.",
|
|
||||||
"succeed-copy-link": "Succeed to copy link to clipboard.",
|
|
||||||
"change-resource-filename": "Change resource filename",
|
|
||||||
"resource-filename-updated": "Resource filename changed.",
|
|
||||||
"invalid-resource-filename": "Invalid filename.",
|
|
||||||
"click-to-save-the-image": "Click to save the image",
|
|
||||||
"generating-the-screenshot": "Generating the screenshot...",
|
|
||||||
"count-selected-resources": "Total selected",
|
|
||||||
"too-short": "Too short",
|
|
||||||
"too-long": "Too long",
|
|
||||||
"not-allow-space": "Don't allow space",
|
|
||||||
"not-allow-chinese": "Don't allow chinese",
|
|
||||||
"succeed-vacuum-database": "Succeed to vacuum database",
|
|
||||||
"succeed-update-additional-style": "Succeed to update additional style",
|
|
||||||
"succeed-copy-resource-link": "Succeed to copy resource link to clipboard",
|
|
||||||
"succeed-update-customized-profile": "Succeed to update customized profile",
|
|
||||||
"succeed-update-additional-script": "Succeed to update additional script",
|
|
||||||
"update-succeed": "Update succeed",
|
|
||||||
"page-not-found": "404 - Page Not Found 😥"
|
|
||||||
},
|
|
||||||
"days": {
|
|
||||||
"mon": "Mon",
|
|
||||||
"tue": "Tue",
|
|
||||||
"wed": "Wed",
|
|
||||||
"thu": "Thu",
|
|
||||||
"fri": "Fri",
|
|
||||||
"sat": "Sat",
|
|
||||||
"sun": "Sun"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +1 @@
|
|||||||
type Locale = "en" | "zh" | "vi" | "fr" | "nl" | "sv" | "de" | "es" | "uk" | "ru" | "it" | "hant" | "tr" | "ko" | "sl";
|
type Locale = string;
|
||||||
|
|||||||
@ -1,7 +1,35 @@
|
|||||||
export const convertLanguageCodeToLocale = (codename: string): Locale => {
|
import i18n, { TLocale, availableLocales } from "@/i18n";
|
||||||
if (codename === "zh-TW" || codename === "zh-HK") {
|
import { FallbackLngObjList } from "i18next";
|
||||||
return "hant";
|
|
||||||
|
export const findNearestLanguageMatch = (codename: string): Locale => {
|
||||||
|
// Find existing translations for full codes (e.g. "en-US", "zh-Hant")
|
||||||
|
if (codename.length > 2 && availableLocales.includes(codename as TLocale)) {
|
||||||
|
return codename as Locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find fallback in src/i18n.ts
|
||||||
|
const i18nfallbacks = Object.entries(i18n.store.options.fallbackLng as FallbackLngObjList);
|
||||||
|
for (const [main, fallbacks] of i18nfallbacks) {
|
||||||
|
if (codename === main) {
|
||||||
|
return fallbacks[0] as Locale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const shortCode = codename.substring(0, 2);
|
const shortCode = codename.substring(0, 2);
|
||||||
return shortCode as Locale;
|
|
||||||
|
// Match existing short code translation
|
||||||
|
if (availableLocales.includes(shortCode as TLocale)) {
|
||||||
|
return shortCode as Locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to match "xx-YY" to existing translation for "xx-ZZ" as a last resort
|
||||||
|
// If some match is undesired, it can be overriden in src/i18n.ts `fallbacks` option
|
||||||
|
for (const existing of availableLocales) {
|
||||||
|
if (shortCode == existing.substring(0, 2)) {
|
||||||
|
return existing as Locale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be "en", so the selector is not empty if there isn't a translation for current user's language
|
||||||
|
return (i18n.store.options.fallbackLng as FallbackLngObjList).default[0] as Locale;
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue