mobile: migrate app to intl

This commit is contained in:
Ammar Ahmed
2024-07-27 10:19:43 +05:00
committed by Abdullah Atta
parent f282265fbb
commit 3e5b740ef0
107 changed files with 6773 additions and 4029 deletions

View File

@@ -35,7 +35,8 @@ const SCOPES = [
"common",
"global",
"docs",
"themebuilder"
"themebuilder",
"intl"
];
module.exports = {

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { i18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import {
THEME_COMPATIBILITY_VERSION,
useThemeEngineStore
@@ -63,7 +65,9 @@ const App = () => {
<View
style={{
height: "100%",
width: "100%"
width: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
<View
@@ -138,7 +142,11 @@ export const withTheme = (Element: () => JSX.Element) => {
currTheme = nextTheme;
}
return <Element />;
return (
<I18nProvider i18n={i18n}>
<Element />
</I18nProvider>
);
};
};

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import Sodium from "@ammarahmed/react-native-sodium";
import { getFileNameWithExtension } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import React from "react";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
@@ -89,7 +90,7 @@ export async function downloadAttachments(attachments) {
current: 0,
total: 0,
success: false,
message: "Download cancelled",
message: strings.network.dowloadCancelled(),
canceled: true
});
return true;
@@ -116,7 +117,7 @@ export async function downloadAttachments(attachments) {
if (isCancelled()) return;
if (!uri) throw new Error("Failed to download file");
if (!uri) throw new Error(strings.failedToDownloadFile());
// Move file to the source folder we will zip eventually and rename the file to it's actual name.
const filePath = `${zipSourceFolder}/${attachment.filename}`;
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, filePath);
@@ -139,7 +140,7 @@ export async function downloadAttachments(attachments) {
useAttachmentStore.getState().setDownloading({
current: 0,
total: 1,
message: "Saving zip file... Please wait",
message: `${strings.savingZipFile()}... ${strings.pleaseWait()}`,
groupId
});
// If all goes well, zip the notesnook-attachments folder in cache.
@@ -149,9 +150,9 @@ export async function downloadAttachments(attachments) {
groupId,
current: progress,
total: 1,
message: `Saving zip file (${(progress * 100).toFixed(
message: `${strings.savingZipFile()} (${(progress * 100).toFixed(
1
)}%)... Please wait`
)}%)... ${strings.pleaseWait()}`
});
});
await zip(zipSourceFolder, zipOutputFile);
@@ -273,8 +274,8 @@ export default async function downloadAttachment(
if (!options.silent) {
ToastManager.show({
heading: "Download successful",
message: filename + " downloaded",
heading: strings.network.downloadSuccess(),
message: strings.network.fileDownloaded(filename),
type: "success"
});
}
@@ -284,12 +285,8 @@ export default async function downloadAttachment(
}
if (!options.silent) {
presentSheet({
title: "File downloaded",
paragraph: `${filename} saved to ${
Platform.OS === "android"
? "selected path"
: "File Manager/Notesnook/downloads"
}`,
title: strings.network.fileDownloaded(),
paragraph: strings.fileSaved(filename, Platform.OS),
icon: "download",
context: global ? null : attachment.hash,
component: <ShareComponent uri={fileUri} name={filename} padding={12} />

View File

@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { strings } from "@notesnook/intl";
import NetInfo from "@react-native-community/netinfo";
import RNFetchBlob from "react-native-blob-util";
import { ToastManager } from "../../services/event-manager";
@@ -48,12 +49,12 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
const size = await getUploadedFileSize(filename);
if (size === -1) {
const error = `Uploaded file verification failed. (File hash: ${filename})`;
const error = `${strings.fileVerificationFailed()} (File hash: ${filename})`;
throw new Error(error);
}
if (size === 0) {
const error = `File length is 0. Please upload this file again from the attachment manager. (File hash: ${filename})`;
const error = `${strings.fileLengthError()} (File hash: ${filename})`;
await db.attachments.markAsFailed(attachment.id, error);
throw new Error(error);
}
@@ -62,7 +63,10 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
const decryptedLength = size - totalChunks * ABYTES;
if (attachment && attachment.size !== decryptedLength) {
const error = `File length mismatch. Expected ${attachment.size} but got ${decryptedLength} bytes. Please upload this file again from the attachment manager. (File hash: ${filename})`;
const error = `${strings.fileLengthMismatch(
attachment.size,
decryptedLength
)} (File hash: ${filename})`;
await db.attachments.markAsFailed(attachment.id, error);
throw new Error(error);
}
@@ -76,7 +80,9 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
DatabaseLogger.log(
`Error downloading file: ${filename}, ${res.status}, ${res.statusText}, reason: Unable to resolve download url`
);
throw new Error(`${res.status}: Unable to resolve download url`);
throw new Error(
`${res.status}: ${strings.failedToResolvedDownloadUrl()}`
);
}
const downloadUrl = await res.text();
@@ -85,7 +91,7 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
DatabaseLogger.log(
`Error downloading file: ${filename}, reason: Unable to resolve download url`
);
throw new Error("Unable to resolve download url");
throw new Error(strings.failedToResolvedDownloadUrl());
}
DatabaseLogger.log(`Download starting: ${filename}`);
@@ -138,18 +144,15 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
return status >= 200 && status < 300;
} catch (e) {
if (e.message !== "canceled" && !e.message.includes("NoSuchKey")) {
ToastManager.show({
heading: "Error downloading file",
const toast = {
heading: strings.downloadError(),
message: e.message,
type: "error",
context: "global"
});
ToastManager.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "local"
});
};
ToastManager.show(toast);
toast.context = "local";
ToastManager.show(toast);
}
useAttachmentStore.getState().remove(filename);

View File

@@ -53,6 +53,7 @@ import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { strings } from "@notesnook/intl";
const getUser = () => {
const user = MMKV.getString("user");
@@ -157,7 +158,7 @@ const AppLockedOverlay = () => {
password.current = undefined;
} else {
ToastManager.show({
heading: `Invalid ${keyboardType === "numeric" ? "pin" : "password"}`,
heading: strings.invalid(keyboardType),
type: "error",
context: "local"
});
@@ -250,7 +251,7 @@ const AppLockedOverlay = () => {
textAlign: "center"
}}
>
Unlock your notes
{strings.unlockNotes()}
</Heading>
<Paragraph
@@ -260,7 +261,7 @@ const AppLockedOverlay = () => {
maxWidth: "90%"
}}
>
{"Please verify it's you"}
{strings.verifyItsYou()}
</Paragraph>
<Seperator />
<View
@@ -301,7 +302,7 @@ const AppLockedOverlay = () => {
{user || appLockHasPasswordSecurity ? (
<>
<Button
title="Continue"
title={strings.continue()}
type="accent"
onPress={onSubmit}
width={250}
@@ -317,7 +318,7 @@ const AppLockedOverlay = () => {
{biometricsAuthEnabled ? (
<Button
title="Unlock with Biometrics"
title={strings.unlockWithBiometrics()}
width={250}
onPress={onUnlockAppRequested}
icon={"fingerprint"}

View File

@@ -57,6 +57,7 @@ import { Notice } from "../ui/notice";
import { Pressable } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
const Actions = ({
attachment,
@@ -81,7 +82,7 @@ const Actions = ({
const actions = [
{
name: "Download",
name: strings.network.download(),
onPress: async () => {
if (currentProgress) {
await db.fs().cancel(attachment.hash);
@@ -93,11 +94,11 @@ const Actions = ({
icon: "download"
},
{
name: "Reupload",
name: strings.network.reupload(),
onPress: async () => {
if (!PremiumService.get()) {
ToastManager.show({
heading: "Upgrade to pro",
heading: strings.upgradeToPro(),
type: "error",
context: "local"
});
@@ -113,10 +114,10 @@ const Actions = ({
icon: "upload"
},
{
name: "Run file check",
name: strings.fileCheck(),
onPress: async () => {
setLoading({
name: "Run file check"
name: strings.fileCheck()
});
const result = await filesystem.checkAttachment(attachment.hash);
if (!result) return;
@@ -125,7 +126,7 @@ const Actions = ({
db.attachments.markAsFailed(attachment.id, result.failed);
setFailed(result.failed);
ToastManager.show({
heading: "File check failed with error: " + result.failed,
heading: strings.fileCheckFailed(result.failed),
type: "error",
context: "local"
});
@@ -134,7 +135,7 @@ const Actions = ({
db.attachments.markAsFailed(attachment.id);
eSendEvent(eDBItemUpdate, attachment.id);
ToastManager.show({
heading: "File check passed",
heading: strings.fileCheckPassed(),
type: "success",
context: "local"
});
@@ -148,12 +149,11 @@ const Actions = ({
icon: "file-check"
},
{
name: "Rename",
onPress: async () => {
name: strings.rename(),
onPress: () => {
presentDialog({
input: true,
title: "Rename file",
paragraph: "Enter a new name for the file",
title: strings.renameFile(),
defaultValue: attachment.filename,
positivePress: async (value) => {
if (value && value.length > 0) {
@@ -172,7 +172,7 @@ const Actions = ({
icon: "form-textbox"
},
{
name: "Delete",
name: strings.delete(),
onPress: async () => {
const relations = await db.relations.to(attachment, "note").get();
await db.attachments.remove(attachment.hash, false);
@@ -256,8 +256,7 @@ const Actions = ({
{notes.length ? (
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
{notes.length} note
{notes.length > 1 ? "s" : ""}
{strings.notes(notes.length)}
</Paragraph>
) : null}
<Paragraph
@@ -265,7 +264,7 @@ const Actions = ({
Clipboard.setString(attachment.hash);
ToastManager.show({
type: "success",
heading: "Attachment hash copied",
heading: strings.hashCopied(),
context: "local"
});
}}
@@ -295,7 +294,7 @@ const Actions = ({
}}
size={SIZE.sm}
>
List of notes:
{strings.listOf()} {strings.dataTypesPlural.note()}:
</Heading>
{notes.map((item) => (
@@ -354,7 +353,7 @@ const Actions = ({
{failed ? (
<Notice
type="alert"
text={`File check failed: ${failed} Try reuploading the file to fix the issue.`}
text={strings.fileCheckFailed(failed)}
size="small"
/>
) : null}

View File

@@ -30,6 +30,7 @@ import { IconButton } from "../ui/icon-button";
import { ProgressCircleComponent } from "../ui/svg/lazy";
import Paragraph from "../ui/typography/paragraph";
import Actions from "./actions";
import { strings } from "@notesnook/intl";
function getFileExtension(filename: string) {
const ext = /^.+\.([^.]+)$/.exec(filename);
@@ -136,7 +137,7 @@ export const AttachmentItem = ({
{!hideWhenNotDownloading ? (
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxs}>
File size: {formatBytes(attachment.size)}
{strings.fileSize()}: {formatBytes(attachment.size)}
</Paragraph>
) : null}
</View>

View File

@@ -24,6 +24,7 @@ import {
SortOptions,
VirtualizedGrouping
} from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { ActivityIndicator, View } from "react-native";
@@ -94,31 +95,31 @@ const useRechecker = create<RecheckerState>((set) => ({
const attachmentTypes = [
{
title: "All",
title: strings.mediaTypes.all(),
filterBy: "all"
},
{
title: "Images",
title: strings.mediaTypes.image(),
filterBy: "images"
},
{
title: "Docs",
filterBy: "documents"
},
{
title: "Video",
filterBy: "video"
},
{
title: "Audio",
title: strings.mediaTypes.audio(),
filterBy: "audio"
},
{
title: "Orphaned",
title: strings.mediaTypes.video(),
filterBy: "video"
},
{
title: strings.mediaTypes.document(),
filterBy: "documents"
},
{
title: strings.mediaTypes.orphaned(),
filterBy: "orphaned"
},
{
title: "Errors",
title: strings.mediaTypes.errors(),
filterBy: "errors"
}
];
@@ -358,7 +359,7 @@ export const AttachmentDialog = ({
paddingHorizontal: 12
}}
>
<Heading>Attachments</Heading>
<Heading>{strings.dataTypesPluralCamelCase.attachment()}</Heading>
<View
style={{

View File

@@ -36,6 +36,7 @@ import { IconButton } from "../ui/icon-button";
import { hideAuth, initialAuthMode } from "./common";
import { Login } from "./login";
import { Signup } from "./signup";
import { strings } from "@notesnook/intl";
export const AuthMode = {
login: 0,
@@ -153,7 +154,7 @@ const AuthModal = () => {
{initialAuthMode.current !== AuthMode.welcomeSignup ? null : (
<Button
title="Skip"
title={strings.skip()}
onPress={() => {
hideAuth();
}}

View File

@@ -35,6 +35,7 @@ import Seperator from "../ui/seperator";
import { Dialog } from "../dialog";
import BackupService from "../../services/backup";
import { sleep } from "../../utils/time";
import { strings } from "@notesnook/intl";
export const ChangePassword = () => {
const passwordInputRef = useRef();
@@ -49,8 +50,8 @@ export const ChangePassword = () => {
const changePassword = async () => {
if (!user?.isEmailConfirmed) {
ToastManager.show({
heading: "Email not confirmed",
message: "Please confirm your email to change account password",
heading: strings.emailNotConfirmed(),
message: strings.emailNotConfirmedDesc(),
type: "error",
context: "local"
});
@@ -58,8 +59,8 @@ export const ChangePassword = () => {
}
if (error || !oldPassword.current || !password.current) {
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again.",
heading: strings.allFieldsRequired(),
message: strings.allFieldsRequiredDesc(),
type: "error",
context: "local"
});
@@ -68,13 +69,13 @@ export const ChangePassword = () => {
setLoading(true);
try {
const result = await BackupService.run(false, "change-password-dialog");
if (result.error)
throw new Error(`Failed to create backup: ${result.error}`);
if (!result.error)
throw new Error(strings.backupFailed() + `: ${result.error}`);
await db.user.clearSessions();
await db.user.changePassword(oldPassword.current, password.current);
ToastManager.show({
heading: "Account password updated",
heading: strings.passwordChangedSuccessfully(),
type: "success",
context: "global"
});
@@ -85,7 +86,7 @@ export const ChangePassword = () => {
} catch (e) {
setLoading(false);
ToastManager.show({
heading: "Failed to change password",
heading: strings.passwordChangeFailed(),
message: e.message,
type: "error",
context: "local"
@@ -102,10 +103,7 @@ export const ChangePassword = () => {
}}
>
<Dialog context="change-password-dialog" />
<DialogHeader
title="Change password"
paragraph="Enter your old and new passwords"
/>
<DialogHeader title={strings.changePassword()} />
<Seperator />
<Input
@@ -138,17 +136,11 @@ export const ChangePassword = () => {
placeholder="New password"
/>
<Notice
text={`Changing password is an irreversible process. You will be logged out from all your devices. Please make sure you do not close the app while your password is changing and have good internet connection.`}
type="alert"
/>
<Notice text={strings.changePasswordNotice()} type="alert" />
<View style={{ height: 10 }} />
<Notice
text={`Once your password is changed, please make sure to save the new account recovery key.`}
type="alert"
/>
<Notice text={strings.changePasswordNotice2()} type="alert" />
<Button
style={{
@@ -158,7 +150,7 @@ export const ChangePassword = () => {
loading={loading}
onPress={changePassword}
type="accent"
title={loading ? null : "I understand, change my password"}
title={loading ? null : strings.changePasswordConfirm()}
/>
</View>
);

View File

@@ -32,6 +32,7 @@ import Input from "../ui/input";
import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const ForgotPassword = () => {
const { colors } = useThemeColors("sheet");
@@ -44,7 +45,7 @@ export const ForgotPassword = () => {
const sendRecoveryEmail = async () => {
if (!email.current || error) {
ToastManager.show({
heading: "Account email is required.",
heading: strings.emailRequired(),
type: "error",
context: "local"
});
@@ -64,8 +65,8 @@ export const ForgotPassword = () => {
lastRecoveryEmailTime: Date.now()
});
ToastManager.show({
heading: "Check your email to reset password",
message: `Recovery email has been sent to ${email.current.toLowerCase()}`,
heading: strings.recoveryEmailSent(),
message: strings.recoveryEmailSentDesc(),
type: "success",
context: "local",
duration: 7000
@@ -75,7 +76,7 @@ export const ForgotPassword = () => {
} catch (e) {
setLoading(false);
ToastManager.show({
heading: "Recovery email not sent",
heading: strings.recoveryEmailFailed(),
message: e.message,
type: "error",
context: "local"
@@ -121,13 +122,13 @@ export const ForgotPassword = () => {
name="email"
size={50}
/>
<Heading>Recovery email sent!</Heading>
<Heading>{strings.recoveryEmailSent()}</Heading>
<Paragraph
style={{
textAlign: "center"
}}
>
Please follow the link in the email to recover your account.
{strings.recoveryEmailSentDesc()}
</Paragraph>
</View>
) : (
@@ -140,10 +141,7 @@ export const ForgotPassword = () => {
padding: 12
}}
>
<DialogHeader
title="Account recovery"
paragraph="We will send you an email with steps on how to reset your password."
/>
<DialogHeader title={strings.accountRecovery()} />
<Seperator />
<Input
@@ -172,7 +170,7 @@ export const ForgotPassword = () => {
loading={loading}
onPress={sendRecoveryEmail}
type="accent"
title={loading ? null : "Next"}
title={loading ? null : strings.next()}
/>
</View>
)}

View File

@@ -38,6 +38,7 @@ import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { useLogin } from "./use-login";
import { strings } from "@notesnook/intl";
const LoginSteps = {
emailAuth: 1,
@@ -147,7 +148,7 @@ export const Login = ({ changeMode }) => {
extraBold
size={SIZE.xxl}
>
Login to your {"\n"}account
{strings.loginToYourAccount()}
</Heading>
</View>
@@ -212,7 +213,7 @@ export const Login = ({ changeMode }) => {
onSubmit={() => login()}
/>
<Button
title="Forgot your password?"
title={strings.forgotPassword()}
style={{
alignSelf: "flex-end",
height: 30,
@@ -249,12 +250,12 @@ export const Login = ({ changeMode }) => {
height={50}
fontSize={SIZE.md}
type="accent"
title={!loading ? "Continue" : null}
title={!loading ? strings.continue() : null}
/>
{step === LoginSteps.passwordAuth && (
<Button
title="Cancel logging in"
title={strings.cancelLogin()}
style={{
alignSelf: "center",
height: 30,
@@ -290,12 +291,12 @@ export const Login = ({ changeMode }) => {
size={SIZE.xs + 1}
color={colors.secondary.paragraph}
>
Don't have an account?{" "}
{strings.dontHaveAccount()}{" "}
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
Sign up
{strings.signUp()}
</Paragraph>
</Paragraph>
</TouchableOpacity>

View File

@@ -47,6 +47,7 @@ import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
import { strings } from "@notesnook/intl";
function getObfuscatedEmail(email) {
if (!email) return "";
@@ -193,15 +194,14 @@ export const SessionExpired = () => {
size={50}
/>
<Heading size={SIZE.xxxl} color={colors.primary.heading}>
Session expired
{strings.sessionExpired()}
</Heading>
<Paragraph
style={{
textAlign: "center"
}}
>
Your session on this device has expired. Please enter password for{" "}
{getObfuscatedEmail(email.current)} to continue.
{strings.sessionExpiredDesc(getObfuscatedEmail(email.current))}
</Paragraph>
</View>
@@ -231,7 +231,7 @@ export const SessionExpired = () => {
loading={loading}
onPress={() => login()}
type="accent"
title={loading ? null : "Login"}
title={loading ? null : strings.login()}
/>
<Button
@@ -242,16 +242,15 @@ export const SessionExpired = () => {
onPress={() => {
presentDialog({
context: "session_expiry",
title: "Logout",
paragraph:
"Are you sure you want to logout from this device? Any unsynced changes will be lost.",
positiveText: "Logout",
title: strings.logoutFromDevice(),
paragraph: strings.logoutDesc(),
positiveText: strings.logout(),
positiveType: "errorShade",
positivePress: logout
});
}}
type="errorShade"
title={loading ? null : "Logout from this device"}
title={loading ? null : strings.logoutFromDevice()}
/>
</View>
<Toast context="local" />

View File

@@ -36,6 +36,7 @@ import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { useSettingStore } from "../../stores/use-setting-store";
import SettingsService from "../../services/settings";
import { strings } from "@notesnook/intl";
export const Signup = ({ changeMode, trial }) => {
const { colors } = useThemeColors();
@@ -54,8 +55,8 @@ export const Signup = ({ changeMode, trial }) => {
const validateInfo = () => {
if (!password.current || !email.current || !confirmPassword.current) {
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again",
heading: strings.allFieldsRequired(),
message: strings.allFieldsRequiredDesc(),
type: "error",
context: "local"
});
@@ -88,7 +89,7 @@ export const Signup = ({ changeMode, trial }) => {
} catch (e) {
setLoading(false);
ToastManager.show({
heading: "Signup failed",
heading: strings.signupFailed(),
message: e.message,
type: "error",
context: "local"
@@ -156,7 +157,7 @@ export const Signup = ({ changeMode, trial }) => {
}}
size={SIZE.xxl}
>
Create your {"\n"}account
{strings.createYourAccount()}
</Heading>
</View>
@@ -235,7 +236,7 @@ export const Signup = ({ changeMode, trial }) => {
size={SIZE.xs}
color={colors.secondary.paragraph}
>
By signing up, you agree to our{" "}
{strings.signupAgreement[0]()}
<Paragraph
size={SIZE.xs}
onPress={() => {
@@ -246,9 +247,9 @@ export const Signup = ({ changeMode, trial }) => {
}}
color={colors.primary.accent}
>
Terms of Service{" "}
{strings.signupAgreement[1]()}
</Paragraph>
and{" "}
{strings.signupAgreement[2]()}
<Paragraph
size={SIZE.xs}
onPress={() => {
@@ -259,14 +260,13 @@ export const Signup = ({ changeMode, trial }) => {
}}
color={colors.primary.accent}
>
Privacy Policy.
</Paragraph>{" "}
You also agree to recieve marketing emails from us which you can
opt-out of from app settings.
{strings.signupAgreement[3]()}
</Paragraph>
{strings.signupAgreement[4]()}
</Paragraph>
<Button
title={!loading ? "Continue" : null}
title={!loading ? strings.continue() : null}
type="accent"
loading={loading}
onPress={signup}
@@ -291,12 +291,12 @@ export const Signup = ({ changeMode, trial }) => {
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.secondary.paragraph}>
Already have an account?{" "}
{strings.alreadyHaveAccount()}{" "}
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
Login
{strings.login()}
</Paragraph>
</Paragraph>
</TouchableOpacity>

View File

@@ -38,6 +38,7 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
import { ScrollView } from "react-native-actions-sheet";
import { strings } from "@notesnook/intl";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
const { colors } = useThemeColors();
@@ -51,20 +52,6 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
const inputRef = useRef();
const [sending, setSending] = useState(false);
const codeHelpText = {
app: "Enter the 6 digit code from your authenticator app to continue logging in",
sms: "Enter the 6 digit code sent to your phone number to continue logging in",
email: "Enter the 6 digit code sent to your email to continue logging in",
recoveryCode: "Enter the recovery code to continue logging in"
};
const secondaryMethodsText = {
app: "I don't have access to authenticator app",
sms: "I don't have access to my phone",
email: "I don't have access to email",
recoveryCode: "I don't have recovery codes"
};
const onNext = async () => {
if (!code.current || code.current.length < 6) return;
setLoading(true);
@@ -94,22 +81,22 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
const methods = [
{
id: "sms",
title: "Send code via SMS",
title: strings.sendCodeSms(),
icon: "message-plus-outline"
},
{
id: "email",
title: "Send code via email",
title: strings.sendCodeEmail(),
icon: "email-outline"
},
{
id: "app",
title: "Enter code from authenticator app",
title: strings.authAppCode(),
icon: "cellphone-key"
},
{
id: "recoveryCode",
title: "I have a recovery code",
title: strings.recoveryCode(),
icon: "key"
}
];
@@ -168,9 +155,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
textAlign: "center"
}}
>
{currentMethod.method
? "Two factor authentication"
: "Select methods for two-factor authentication"}
{currentMethod.method ? strings["2fa"]() : strings.select2faMethod()}
</Heading>
<Paragraph
style={{
@@ -178,8 +163,8 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
textAlign: "center"
}}
>
{codeHelpText[currentMethod.method] ||
"Select how you would like to recieve the code"}
{strings["2faCodeHelpText"][currentMethod.method]() ||
strings.select2faCodeHelpText()}
</Paragraph>
<Seperator />
@@ -191,7 +176,11 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
title={
sending
? ""
: `${seconds ? `Resend code in (${seconds})` : "Send code"}`
: `${
seconds
? strings.resend2faCode(seconds)
: strings.sendCode()
}`
}
loading={sending}
height={30}
@@ -240,7 +229,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
/>
<Seperator />
<Button
title={loading ? null : "Next"}
title={loading ? null : strings.next()}
type="accent"
width={250}
loading={loading}
@@ -252,7 +241,9 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
/>
<Button
title={secondaryMethodsText[currentMethod.method]}
title={
strings["2faCodeSecondaryMethodText"][currentMethod.method]
}
type="plain"
onPress={onRequestSecondaryMethod}
height={30}

View File

@@ -26,6 +26,7 @@ import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet } from "../../utils/events";
import TwoFactorVerification from "./two-factor";
import { strings } from "@notesnook/intl";
export const LoginSteps = {
emailAuth: 1,
@@ -49,8 +50,8 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
(!email.current && step === LoginSteps.emailAuth)
) {
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again",
heading: strings.allFieldsRequired(),
message: strings.allFieldsRequiredDesc(),
type: "error",
context: "local"
});
@@ -122,7 +123,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
setLoading(false);
ToastManager.show({
heading: "Login failed",
heading: strings.loginFailed(),
message: e.message,
type: "error",
context: "local"
@@ -136,8 +137,8 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
setUser(user);
clearMessage();
ToastManager.show({
heading: "Login successful",
message: `Logged in as ${user.email}`,
heading: strings.loginSuccess(),
message: strings.loginSuccessDesc(),
type: "success",
context: "global"
});

View File

@@ -26,12 +26,13 @@ import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Paragraph from "../ui/typography/paragraph";
import { getColorLinearShade } from "../../utils/colors";
import { strings } from "@notesnook/intl";
const DialogButtons = ({
onPressPositive,
onPressNegative,
positiveTitle,
negativeTitle = "Cancel",
negativeTitle = strings.cancel(),
loading,
doneText,
positiveType

View File

@@ -38,6 +38,7 @@ import { useCallback } from "react";
import { Button } from "../ui/button";
import { getContainerBorder } from "../../utils/colors";
import { Notice } from "../ui/notice";
import { strings } from "@notesnook/intl";
export const Dialog = ({ context = "global" }) => {
const { colors } = useThemeColors();
@@ -50,8 +51,8 @@ export const Dialog = ({ context = "global" }) => {
const [dialogInfo, setDialogInfo] = useState({
title: "",
paragraph: "",
positiveText: "Done",
negativeText: "Cancel",
positiveText: strings.done(),
negativeText: strings.cancel(),
positivePress: () => {},
onClose: () => {},
positiveType: "transparent",

View File

@@ -49,6 +49,7 @@ import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import { strings } from "@notesnook/intl";
export const AppLockPassword = () => {
const { colors } = useThemeColors();
@@ -119,22 +120,7 @@ export const AppLockPassword = () => {
}}
>
<DialogHeader
title={
mode === "change"
? `Change app lock ${keyboardType}`
: mode === "remove"
? `Remove app lock ${keyboardType}`
: `Set up app lock ${keyboardType}`
}
paragraph={
mode === "change"
? `Change app lock ${keyboardType}`
: mode === "remove"
? `Enter ${
accountPass ? "account password" : `app lock ${keyboardType}`
} to remove ${keyboardType}`
: `Set up a custom app lock ${keyboardType} to unlock the app`
}
title={strings.changeAppLockCredentials(mode, keyboardType)}
icon="shield"
padding={12}
/>
@@ -253,7 +239,7 @@ export const AppLockPassword = () => {
iconColor={
accountPass ? colors.primary.accent : colors.primary.icon
}
title="Use account password instead"
title={strings.useAccountPassword()}
style={{
width: "100%",
alignSelf: "flex-start",
@@ -272,7 +258,7 @@ export const AppLockPassword = () => {
if (mode === "create") {
if (!values.current.password || !values.current.confirmPassword) {
ToastManager.error(
new Error("All inputs are required"),
new Error(strings.allFieldsRequired()),
undefined,
"local"
);
@@ -281,11 +267,7 @@ export const AppLockPassword = () => {
if (values.current.password !== values.current.confirmPassword) {
ToastManager.error(
new Error(
`${
keyboardType === "pin" ? "Pin" : "Password"
} does not match`
),
new Error(strings.mismatch(keyboardType)),
undefined,
"local"
);
@@ -301,7 +283,7 @@ export const AppLockPassword = () => {
!values.current.confirmPassword
) {
ToastManager.error(
new Error("All inputs are required"),
new Error(strings.allFieldsRequired()),
undefined,
"local"
);
@@ -310,11 +292,7 @@ export const AppLockPassword = () => {
if (values.current.password !== values.current.confirmPassword) {
ToastManager.error(
new Error(
`${
keyboardType === "pin" ? "Pin" : "Password"
} does not match`
),
new Error(strings.mismatch(keyboardType)),
undefined,
"local"
);
@@ -326,9 +304,7 @@ export const AppLockPassword = () => {
if (!isCurrentPasswordCorrect) {
ToastManager.error(
new Error(
`${keyboardType === "pin" ? "Pin" : "Password"} incorrect`
),
new Error(strings.incorrect(keyboardType)),
undefined,
"local"
);
@@ -342,7 +318,7 @@ export const AppLockPassword = () => {
} else if (mode === "remove") {
if (!values.current.password) {
ToastManager.error(
new Error("All inputs are required"),
new Error(strings.allFieldsRequired()),
undefined,
"local"
);
@@ -357,10 +333,8 @@ export const AppLockPassword = () => {
ToastManager.error(
new Error(
accountPass
? "Account password incorrect"
: `${
keyboardType === "pin" ? "Pin" : "Password"
} incorrect`
? strings.passwordIncorrect()
: strings.incorrect(keyboardType)
),
undefined,
"local"
@@ -377,7 +351,7 @@ export const AppLockPassword = () => {
SettingsService.setProperty("appLockEnabled", false);
SettingsService.setPrivacyScreen(SettingsService.get());
ToastManager.show({
message: "App lock disabled",
message: strings.applockDisabled(),
type: "success"
});
}
@@ -391,9 +365,13 @@ export const AppLockPassword = () => {
close();
}}
positiveTitle={
mode === "remove" ? "Remove" : mode === "change" ? "Change" : "Save"
mode === "remove"
? strings.remove()
: mode === "change"
? strings.change()
: strings.save()
}
negativeTitle="Cancel"
negativeTitle={strings.cancel()}
positiveType="transparent"
loading={false}
doneText=""

View File

@@ -32,6 +32,7 @@ import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import { Notice } from "../../ui/notice";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export default function AttachImage({
response,
@@ -64,7 +65,7 @@ export default function AttachImage({
}}
>
<Paragraph style={{ color: colors.primary.paragraph, marginBottom: 6 }}>
Attaching {response?.length} image(s):
{strings.attachImageHeading(response?.length || 1)}
</Paragraph>
<ScrollView horizontal>
{response?.map?.((item) => (
@@ -123,14 +124,14 @@ export default function AttachImage({
}}
size={SIZE.sm}
>
Compress (recommended)
{strings.compress()} ({strings.recommended().toLowerCase()})
</Paragraph>
</TouchableOpacity>
{!compress ? (
<Notice
type="alert"
text="Images uploaded without compression are slow to load and take more bandwidth. We recommend compressing images unless you need image in original quality."
text={strings.compressionOffNotice()}
size="small"
style={{
width: "100%",
@@ -140,7 +141,7 @@ export default function AttachImage({
) : (
<Notice
type="information"
text="Compressed images are uploaded in Full HD resolution and usually are good enough for most use cases."
text={strings.compressionOnNotice()}
size="small"
style={{
width: "100%",
@@ -150,9 +151,7 @@ export default function AttachImage({
)}
<Button
title={`${
(response?.length || 0) > 1 ? "Attach Images" : "Attach Image"
}`}
title={strings.attachImageHeading(response?.length || 1)}
type="accent"
width="100%"
onPress={() => {

View File

@@ -33,6 +33,7 @@ import { Toast } from "../../toast";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import { Pressable } from "../../ui/pressable";
import { strings } from "@notesnook/intl";
const HEX_COLOR_REGEX_ALPHA =
/^#(?:(?:[\da-fA-F]{3}){1,2}|(?:[\da-fA-F]{4}){1,2})$/;
@@ -152,27 +153,27 @@ const ColorPicker = ({
/>
<Button
title="Add color"
title={strings.addColor()}
style={{
marginBottom: 10
}}
onPress={async () => {
if (!selectedColor)
return ToastManager.error(
new Error("Select a color"),
new Error(strings.noColorSelected()),
undefined,
"color-picker"
);
if (!title.current)
return ToastManager.error(
new Error("Enter a title for the color")
new Error(strings.allFieldsRequired())
);
const exists = await db.colors.all.find((v) =>
v.and([v(`colorCode`, "==", selectedColor)])
);
if (exists)
return ToastManager.error(
new Error(`Color #${selectedColor} already exists`)
new Error(strings.colorExists(selectedColor))
);
const id = await db.colors.add({
title: title.current,

View File

@@ -45,6 +45,7 @@ import Paragraph from "../../ui/typography/paragraph";
import { sleep } from "../../../utils/time";
import { MMKV } from "../../../common/database/mmkv";
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
import { strings } from "@notesnook/intl";
const WIN_WIDTH = Dimensions.get("window").width;
const WIN_HEIGHT = Dimensions.get("window").height;
@@ -137,10 +138,10 @@ const PDFPreview = () => {
presentDialog({
context: attachment?.hash,
input: true,
inputPlaceholder: "Enter password",
positiveText: "Unlock",
title: "Decrypt",
paragraph: "Please input password to view pdf.",
inputPlaceholder: strings.enterPassword(),
positiveText: strings.open(),
title: strings.pdfLocked(),
paragraph: strings.pdfLockedDesc(),
positivePress: (value) => {
setTimeout(() => {
setPassword(value);
@@ -187,8 +188,7 @@ const PDFPreview = () => {
}}
color={colors.static.white}
>
Loading {`${progress?.percent ? `(${progress?.percent})` : ""}`}
... Please wait
{strings.loadingWithProgress(progress.percent)}
</Paragraph>
</Animated.View>
) : (

View File

@@ -52,6 +52,7 @@ import { Button } from "../../ui/button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export class VaultDialog extends Component {
constructor(props) {
@@ -76,7 +77,7 @@ export class VaultDialog extends Component {
changePassword: false,
copyNote: false,
revokeFingerprintAccess: false,
title: "Unlock Note",
title: strings.goToEditor(),
description: null,
clearVault: false,
deleteVault: false,
@@ -91,46 +92,25 @@ export class VaultDialog extends Component {
this.password = null;
this.confirmPassword = null;
this.newPassword = null;
(this.title = !this.state.novault
? "Create Vault"
this.title = !this.state.novault
? strings.createVault()
: this.state.fingerprintAccess
? "Vault Fingerprint Unlock"
? strings.vaultFingerprintUnlock()
: this.state.revokeFingerprintAccess
? "Revoke Vault Fingerprint Unlock"
? strings.revokeVaultFingerprintUnlock()
: this.state.changePassword
? "Change Vault Password"
? strings.changeVaultPassword()
: this.state.noteLocked
? this.state.deleteNote
? "Delete note"
? strings.deleteNote()
: this.state.share
? "Share note"
? strings.shareNote()
: this.state.copyNote
? "Copy note"
? strings.copyNote()
: this.state.goToEditor
? "Unlock note"
: "Unlock note"
: "Lock note"),
(this.description = !this.state.novault
? "Set a password to create vault"
: this.state.fingerprintAccess
? "Enter vault password to enable fingerprint unlocking."
: this.state.revokeFingerprintAccess
? "Disable vault fingerprint unlock "
: this.state.changePassword
? "Setup a new password for the vault."
: this.state.permanant
? "Enter password to remove note from vault."
: this.state.noteLocked
? this.state.deleteNote
? "Unlock note to delete it. If biometrics are not working, you can enter device pin to unlock vault."
: this.state.share
? "Unlock note to share it. If biometrics are not working, you can enter device pin to unlock vault."
: this.state.copyNote
? "Unlock note to copy it. If biometrics are not working, you can enter device pin to unlock vault."
: this.state.goToEditor
? "Unlock note to open it in editor."
: "Enter vault password to unlock note. If biometrics are not working, you can enter device pin to unlock vault."
: "Enter vault password to lock note. If biometrics are not working, you can enter device pin to lock note.");
? strings.goToEditor()
: strings.goToEditor()
: strings.lockNote();
}
componentDidMount() {
@@ -708,12 +688,7 @@ export class VaultDialog extends Component {
paddingTop: 12
}}
>
<DialogHeader
title={this.state.title}
paragraph={this.state.description}
icon="shield"
padding={12}
/>
<DialogHeader title={this.state.title} icon="shield" padding={12} />
<Seperator half />
<View
@@ -761,11 +736,11 @@ export class VaultDialog extends Component {
changePassword ? null : (
<Button
onPress={() =>
this._onPressFingerprintAuth("Unlock note", "")
this._onPressFingerprintAuth(strings.unlockNote(), "")
}
icon="fingerprint"
width="100%"
title={"Biometric unlock"}
title={strings.unlockWithBiometrics()}
type="transparent"
/>
)}
@@ -788,7 +763,7 @@ export class VaultDialog extends Component {
marginTop: 10
}}
width="100%"
title={"Delete all notes"}
title={strings.deleteAllNotes()}
type="errorShade"
/>
)}
@@ -867,9 +842,7 @@ export class VaultDialog extends Component {
{this.state.biometricUnlock &&
!this.state.isBiometryEnrolled &&
novault ? (
<Paragraph>
Unlock with password once to enable biometric access.
</Paragraph>
<Paragraph>{strings.vaultEnableBiometrics()}</Paragraph>
) : null}
{this.state.isBiometryAvailable &&
@@ -888,7 +861,7 @@ export class VaultDialog extends Component {
}}
icon="fingerprint"
width="100%"
title="Biometric unlocking"
title={strings.unlockWithBiometrics()}
iconColor={
this.state.biometricUnlock
? colors.selected.accent
@@ -908,26 +881,26 @@ export class VaultDialog extends Component {
}
positiveTitle={
deleteVault
? "Delete"
? strings.deleteVault()
: clearVault
? "Clear"
? strings.clearVault()
: fingerprintAccess
? "Enable"
? strings.enable()
: this.state.revokeFingerprintAccess
? "Revoke"
? strings.revoke()
: changePassword
? "Change"
? strings.change()
: this.state.noteLocked
? deleteNote
? "Delete"
? strings.delete()
: share
? "Share "
? strings.share()
: goToEditor
? "Open"
: "Unlock"
? strings.open()
: strings.unlock()
: !note.id
? "Create"
: "Lock"
? strings.create()
: strings.lock()
}
/>
</View>

View File

@@ -40,6 +40,7 @@ import Navigation from "../../services/navigation";
import { eOpenLoginDialog } from "../../utils/events";
import { AuthMode } from "../auth";
import { eSendEvent } from "../../services/event-manager";
import { strings } from "@notesnook/intl";
const Intro = ({ navigation }) => {
const { colors } = useThemeColors();
@@ -212,7 +213,7 @@ const Intro = ({ navigation }) => {
}}
fontSize={SIZE.md}
type="accent"
title="Get started"
title={strings.getStarted()}
/>
</View>
</ScrollView>

View File

@@ -29,6 +29,7 @@ import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
import { strings } from "@notesnook/intl";
export const NotebookHeader = ({
notebook,
@@ -148,11 +149,7 @@ export const NotebookHeader = ({
size={SIZE.xs}
color={colors.secondary.paragraph}
>
{notebook && totalNotes > 1
? totalNotes + " notes"
: totalNotes === 1
? totalNotes + " note"
: "0 notes"}
{strings.notes(totalNotes || 0)}
</Paragraph>
</View>
);

View File

@@ -32,6 +32,7 @@ import Sort from "../../sheets/sort";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import { strings } from "@notesnook/intl";
type SectionHeaderProps = {
item: GroupHeader;
@@ -103,7 +104,7 @@ export const SectionHeader = React.memo<
textAlignVertical: "center"
}}
>
{!item.title || item.title === "" ? "Pinned" : item.title}
{!item.title || item.title === "" ? strings.pinned() : item.title}
</Heading>
</TouchableOpacity>

View File

@@ -46,6 +46,7 @@ import { ReminderTime } from "../../ui/reminder-time";
import { TimeSince } from "../../ui/time-since";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
type NoteItemProps = {
item: Note | BaseTrashItem<Note>;
@@ -341,9 +342,10 @@ const NoteItem = ({
marginRight: 6
}}
>
Deleted on{" "}
{item && item.dateDeleted
? new Date(item.dateDeleted).toISOString().slice(0, 10)
? strings.deletedOn(
new Date(item.dateDeleted).toISOString().slice(0, 10)
)
: null}
</Paragraph>
@@ -354,8 +356,7 @@ const NoteItem = ({
marginRight: 6
}}
>
{(item as TrashItem).itemType[0].toUpperCase() +
(item as TrashItem).itemType.slice(1)}
{strings.note()}
</Paragraph>
</>
)}

View File

@@ -30,6 +30,7 @@ import { Properties } from "../../properties";
import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
type NotebookItemProps = {
item: Notebook | BaseTrashItem<Notebook>;
@@ -112,10 +113,11 @@ export const NotebookItem = ({
marginRight: 6
}}
>
{"Deleted on " +
{strings.deletedOn(
new Date((item as TrashItem).dateDeleted)
.toISOString()
.slice(0, 10)}
.slice(0, 10)
)}
</Paragraph>
<Paragraph
color={colors.primary.accent}
@@ -147,11 +149,7 @@ export const NotebookItem = ({
marginRight: 6
}}
>
{item && totalNotes > 1
? totalNotes + " notes"
: totalNotes === 1
? totalNotes + " note"
: "0 notes"}
{strings.notes(totalNotes)}
</Paragraph>
{item.pinned ? (
@@ -183,11 +181,7 @@ export const NotebookItem = ({
marginRight: 6
}}
>
{item && totalNotes > 1
? totalNotes + " notes"
: totalNotes === 1
? totalNotes + " note"
: "0 notes"}
{strings.notes(totalNotes)}
</Paragraph>
</>
) : null}

View File

@@ -30,6 +30,7 @@ import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
import { strings } from "@notesnook/intl";
const ReminderItem = React.memo(
({
@@ -108,7 +109,7 @@ const ReminderItem = React.memo(
color={colors.secondary.paragraph}
style={{ marginLeft: 5 }}
>
Disabled
{strings.disabled()}
</Paragraph>
</View>
) : null}
@@ -137,9 +138,7 @@ const ReminderItem = React.memo(
color={colors.secondary.paragraph}
style={{ marginLeft: 5 }}
>
{item.recurringMode.slice(0, 1).toUpperCase() +
item.recurringMode.replace("y", "i").slice(1) +
"ly"}
{strings.reminderRecurringMode[item.recurringMode]()}
</Paragraph>
</View>
) : null}

View File

@@ -29,6 +29,7 @@ import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
import { strings } from "@notesnook/intl";
const TagItem = React.memo(
({
@@ -77,11 +78,7 @@ const TagItem = React.memo(
marginTop: 5
}}
>
{totalNotes > 1
? totalNotes + " notes"
: totalNotes === 1
? totalNotes + " note"
: null}
{strings.notes(totalNotes)}
</Paragraph>
</View>

View File

@@ -48,6 +48,7 @@ import { IconButton } from "../ui/icon-button";
import Seperator from "../ui/seperator";
import Paragraph from "../ui/typography/paragraph";
import { diff } from "diffblazer";
import { strings } from "@notesnook/intl";
const MergeConflicts = () => {
const { colors } = useThemeColors();
@@ -171,7 +172,11 @@ const MergeConflicts = () => {
fontWeight: "bold"
}}
>
{isCurrent ? "(This Device)" : "(Incoming)"}
(
{isCurrent
? strings.mergeConflict.thisDevice()
: strings.mergeConflict.otherDevice()}
)
</Text>
{"\n"}
{getFormattedDate(contentToKeep?.dateEdited)}
@@ -191,7 +196,7 @@ const MergeConflicts = () => {
setCopy(contentToKeep);
setDialogVisible(true);
}}
title="Save a copy"
title={strings.saveACopy()}
type="secondary"
height={30}
style={{
@@ -204,7 +209,7 @@ const MergeConflicts = () => {
<View style={{ width: 10 }} />
{isDiscarded ? (
<Button
title="Discard"
title={strings.discard()}
type="accent"
buttonType={{
color: colors.static.red,
@@ -234,7 +239,9 @@ const MergeConflicts = () => {
}}
type="accent"
fontSize={SIZE.xs}
title={keeping && !isDiscarded ? "Undo" : "Keep"}
title={
keeping && !isDiscarded ? strings.undo() : strings.keep()
}
onPress={() => {
setKeep(keeping && !isDiscarded ? null : contentToKeep);
}}
@@ -277,11 +284,7 @@ const MergeConflicts = () => {
{dialogVisible && (
<BaseDialog visible={true}>
<DialogContainer>
<DialogHeader
title="Apply Changes"
paragraph="Apply selected changes to note?"
padding={12}
/>
<DialogHeader title={strings.applyChanges()} padding={12} />
<Seperator />
<DialogButtons
positiveTitle="Apply"

View File

@@ -37,6 +37,7 @@ import { Pressable } from "../ui/pressable";
import Seperator from "../ui/seperator";
import Paragraph from "../ui/typography/paragraph";
import NotePreview from "./preview";
import { strings } from "@notesnook/intl";
const HistoryItem = ({
index,
@@ -141,11 +142,7 @@ export default function NoteHistory({
return (
<View>
<SheetProvider context="note_history" />
<DialogHeader
title="Note history"
paragraph="Revert back to an older version of this note"
padding={12}
/>
<DialogHeader title={strings.noteHistory()} padding={12} />
<Seperator />
@@ -180,7 +177,7 @@ export default function NoteHistory({
<>
<Icon name="history" size={50} color={colors.primary.icon} />
<Paragraph color={colors.secondary.paragraph}>
No note history found on this device.
{strings.noteHistoryPlaceholder()}
</Paragraph>
</>
)}
@@ -196,7 +193,7 @@ export default function NoteHistory({
alignSelf: "center"
}}
>
Note version history is local only.{" "}
{strings.noteHistoryNotice[0]()}{" "}
<Text
onPress={() => {
openLinkInBrowser(
@@ -208,7 +205,7 @@ export default function NoteHistory({
textDecorationLine: "underline"
}}
>
Learn how this works.
{strings.noteHistoryNotice[1]()}
</Text>
</Paragraph>
</View>

View File

@@ -35,6 +35,7 @@ import { presentDialog } from "../dialog/functions";
import { Button } from "../ui/button";
import Paragraph from "../ui/typography/paragraph";
import { diff } from "diffblazer";
import { strings } from "@notesnook/intl";
/**
*
@@ -150,7 +151,7 @@ export default function NotePreview({ session, content, note }) {
}}
>
<Paragraph color={colors.secondary.paragraph}>
Preview not available, content is encrypted.
{strings.encryptedNoteHistoryNotice()}
</Paragraph>
</View>
)}
@@ -160,10 +161,15 @@ export default function NotePreview({ session, content, note }) {
paddingHorizontal: 12
}}
>
<Button onPress={restore} title="Restore" type="accent" width="100%" />
<Button
onPress={restore}
title={strings.restore()}
type="accent"
width="100%"
/>
<Button
onPress={deleteNote}
title="Delete permanently"
title={strings.deletePermanently()}
type="error"
width="100%"
style={{

View File

@@ -36,6 +36,7 @@ import ColorPicker from "../dialogs/color-picker";
import { Button } from "../ui/button";
import NativeTooltip from "../../utils/tooltip";
import { Pressable } from "../ui/pressable";
import { strings } from "@notesnook/intl";
const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
const { colors } = useThemeColors();
@@ -145,7 +146,7 @@ export const ColorTags = ({ item }: { item: Note }) => {
buttonType={{
text: colors.primary.accent
}}
title="Add color"
title={strings.addColor()}
type="secondary"
icon="plus"
iconPosition="right"

View File

@@ -23,26 +23,10 @@ import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import Paragraph from "../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
import { strings } from "@notesnook/intl";
export const DateMeta = ({ item }) => {
const { colors } = useThemeColors();
const getNameFromKey = (key) => {
switch (key) {
case "dateCreated":
return "Created at:";
case "dateEdited":
return "Last edited at:";
case "dateModified":
return "Last modified at:";
case "dateDeleted":
return "Deleted at:";
case "dateUploaded":
return "Uploaded at:";
default:
return key;
}
};
function getDateMeta() {
let keys = Object.keys(item);
if (keys.includes("dateEdited"))
@@ -64,7 +48,7 @@ export const DateMeta = ({ item }) => {
}}
>
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
{getNameFromKey(key)}
{strings.dateDescFromKey(key)}
</Paragraph>
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
{getFormattedDate(item[key], "date-time")}

View File

@@ -39,6 +39,7 @@ import Notebooks from "./notebooks";
import { Synced } from "./synced";
import { TagStrip, Tags } from "./tags";
import { tabBarRef } from "../../utils/global-refs";
import { strings } from "@notesnook/intl";
const Line = ({ top = 6, bottom = 6 }) => {
const { colors } = useThemeColors();
@@ -61,7 +62,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
if (!item || !item.id) {
return (
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
Start writing to save your note.
{strings.noNotePropertiesNotice()}
</Paragraph>
);
}

View File

@@ -29,6 +29,7 @@ import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import { Pressable } from "../ui/pressable";
import { strings } from "@notesnook/intl";
export default function Notebooks({ note, close, full }) {
const { colors } = useThemeColors();
@@ -102,7 +103,7 @@ export default function Notebooks({ note, close, full }) {
{noteNotebooks.length > 1 && !full ? (
<Button
title={`View all linked notebooks`}
title={strings.viewAllLinkedNotebooks()}
fontSize={SIZE.xs}
style={{
alignSelf: "flex-end",

View File

@@ -28,6 +28,7 @@ import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const Synced = ({ item, close }) => {
const { colors } = useThemeColors();
const user = useUserStore((state) => state.user);
@@ -71,7 +72,7 @@ export const Synced = ({ item, close }) => {
flexWrap: "wrap"
}}
>
Encrypted and synced
{strings.noteSyncedNoticeHeading()}
</Heading>
{shouldShrink ? null : (
<Paragraph
@@ -81,7 +82,7 @@ export const Synced = ({ item, close }) => {
size={SIZE.xs}
color={colors.primary.paragraph}
>
No one can view this {item.itemType || item.type} except you.
{strings.noteSyncedNoticeDesc(item.itemType || item.type)}
</Paragraph>
)}
</View>
@@ -99,7 +100,7 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
title="Learn more"
title={strings.learnMore()}
fontSize={SIZE.xs}
height={30}
type="secondaryAccented"

View File

@@ -27,6 +27,7 @@ import { sleep } from "../../utils/time";
import ManageTagsSheet from "../sheets/manage-tags";
import { Button } from "../ui/button";
import { ColorTags } from "./color-tags";
import { strings } from "@notesnook/intl";
export const Tags = ({ item, close }) => {
const { colors } = useThemeColors();
@@ -51,7 +52,7 @@ export const Tags = ({ item, close }) => {
buttonType={{
text: colors.primary.accent
}}
title="Add tag"
title={strings.addTag()}
type="secondary"
icon="plus"
iconPosition="right"

View File

@@ -40,6 +40,7 @@ import { MoveNotes } from "../move-notes/movenote";
import { eOnNotebookUpdated } from "../../../utils/events";
import { getParentNotebookId } from "../../../utils/notebooks";
import { useNotebookStore } from "../../../stores/use-notebook-store";
import { strings } from "@notesnook/intl";
export const AddNotebookSheet = ({
notebook,
@@ -119,7 +120,7 @@ export const AddNotebookSheet = ({
}}
>
<Heading size={SIZE.lg}>
{notebook ? "Edit Notebook" : "New Notebook"}
{notebook ? strings.editNotebook() : strings.newNotebook()}
</Heading>
<Seperator />
@@ -157,7 +158,7 @@ export const AddNotebookSheet = ({
/>
<Button
title={notebook ? "Save" : "Add"}
title={notebook ? strings.save() : strings.add()}
type="accent"
height={45}
fontSize={SIZE.md}

View File

@@ -55,6 +55,7 @@ import { useNotebookItemSelectionStore } from "./store";
import { AddNotebookSheet } from "../add-notebook";
import Input from "../../ui/input";
import { presentDialog } from "../../dialog/functions";
import { strings } from "@notesnook/intl";
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations
@@ -200,11 +201,11 @@ const MoveNoteSheet = ({
minHeight: 10,
flexShrink: 1
}}
title="Select notebooks"
title={strings.selectNotebooks()}
paragraph={
!multiSelect
? "Long press to enable multi-select."
: "Select notebooks you want to add note(s) to."
? strings.enableMultiSelect()
: strings.selectNotebooksDesc()
}
/>
@@ -236,7 +237,7 @@ const MoveNoteSheet = ({
paddingHorizontal: 24,
alignSelf: "flex-start"
}}
title="Save"
title={strings.save()}
type={"accent"}
onPress={onSave}
/>
@@ -319,7 +320,7 @@ const MoveNoteSheet = ({
/>
) : (
<Paragraph color={colors.primary.icon}>
No notebooks
{strings.emptyPlaceholders("notebook")}
</Paragraph>
)}
</View>

View File

@@ -30,6 +30,7 @@ import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import { eUserLoggedIn } from "../../../utils/events";
import { strings } from "@notesnook/intl";
type ChangeEmailProps = {
actionSheetRef: RefObject<ActionSheetRef>;
@@ -103,8 +104,8 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
return (
<View style={{ paddingHorizontal: 12 }}>
<DialogHeader
title="Change email"
paragraph="Your account email will be changed without affecting your subscription or any other settings."
title={strings.changeEmail()}
paragraph={strings.changeEmailDesc()}
/>
<View
style={{
@@ -149,8 +150,8 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
loading
? undefined
: step === EmailChangeSteps.verify
? "Verify"
: "Change email"
? strings.verify()
: strings.changeEmail()
}
type="accent"
width={250}

View File

@@ -35,6 +35,7 @@ import { IconButton } from "../../ui/icon-button";
import { Pressable } from "../../ui/pressable";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
type TabItem = {
id: number;
@@ -137,7 +138,9 @@ const TabItemComponent = (props: {
numberOfLines={1}
size={SIZE.md}
>
{props.tab.noteId ? item?.title || "Untitled note" : "New note"}
{props.tab.noteId
? item?.title || strings.untitledNote()
: strings.newNote()}
</Paragraph>
</View>
@@ -229,7 +232,7 @@ export default function EditorTabs({
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>Tabs</Heading>
<Heading size={SIZE.lg}>{strings.tabs()}</Heading>
<Button
onPress={() => {
useTabStore.getState().newTab();
@@ -240,7 +243,7 @@ export default function EditorTabs({
}, 500);
close?.();
}}
title="New tab"
title={strings.newTab()}
icon="plus"
style={{
flexDirection: "row",

View File

@@ -52,6 +52,7 @@ import { Pressable } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
const ExportNotesSheet = ({
ids,
@@ -175,14 +176,7 @@ const ExportNotesSheet = ({
>
<DialogHeader
icon="export"
title={
ids.length > 1 ? `Export ${ids.length} Notes` : "Export Note"
}
paragraph={`All exports are saved in ${
Platform.OS === "android"
? "the selected"
: "Notesnook/exported"
} folder in phone storage`}
title={`${strings.export()} ${strings.notes(ids.length)}`}
/>
</View>
@@ -263,11 +257,7 @@ const ExportNotesSheet = ({
{!complete ? (
<>
<ActivityIndicator />
<Paragraph>
{`${
status ? `${status})` : `Exporting notes`
}... Please wait`}
</Paragraph>
<Paragraph>{strings.exportingNotes(status)}</Paragraph>
</>
) : (
<>
@@ -287,21 +277,20 @@ const ExportNotesSheet = ({
}}
color={colors.secondary.heading}
>
{ids.length > 1
? `${ids.length} Notes exported`
: "Note exported"}
{strings.exportSuccessHeading(ids.length)}
</Heading>
<Paragraph
style={{
textAlign: "center"
}}
>
Your {ids.length > 1 ? "notes are" : "note is"} exported
successfully as {result?.fileName}
{strings.exportSuccessDesc(result?.fileName as string)}
</Paragraph>
<Button
title={
Platform.OS === "android" ? "Open file location" : "Open"
Platform.OS === "android"
? strings.openFileLocation()
: strings.open()
}
type="accent"
width={250}
@@ -332,7 +321,7 @@ const ExportNotesSheet = ({
}}
/>
<Button
title="Share"
title={strings.share()}
type="secondaryAccented"
width={250}
fontSize={SIZE.md}
@@ -356,7 +345,7 @@ const ExportNotesSheet = ({
}}
/>
<Button
title="Export in another format"
title={strings.exportAgain()}
type="inverted"
width={250}
fontSize={SIZE.md}

View File

@@ -23,6 +23,7 @@ import FileViewer from "react-native-file-viewer";
import { ToastManager } from "../../../services/event-manager";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import { strings } from "@notesnook/intl";
export const ShareComponent = ({ uri, name, padding }) => {
return (
<View
@@ -31,7 +32,7 @@ export const ShareComponent = ({ uri, name, padding }) => {
}}
>
<Button
title="Open"
title={strings.open()}
type="accent"
width="100%"
fontSize={SIZE.md}
@@ -50,7 +51,7 @@ export const ShareComponent = ({ uri, name, padding }) => {
}}
/>
<Button
title="Share"
title={strings.share()}
type="shade"
width="100%"
fontSize={SIZE.md}

View File

@@ -34,6 +34,7 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const Issue = ({ defaultTitle, defaultBody, issueTitle }) => {
const { colors } = useThemeColors();
@@ -99,14 +100,14 @@ Logged in: ${user ? "yes" : "no"}`,
gap: 10
}}
>
<Heading>Issue submitted</Heading>
<Heading>{strings.issueCreatedHeading()}</Heading>
<Paragraph
style={{
textAlign: "center"
}}
selectable={true}
>
You can track your issue at{" "}
{strings.issueCreatedDesc[0]()}
<Paragraph
style={{
textDecorationLine: "underline",
@@ -118,12 +119,11 @@ Logged in: ${user ? "yes" : "no"}`,
>
{issueUrl.current}
</Paragraph>
. Please note that we will respond to your issue on the given
link. We recommend that you save it.
. {strings.issueCreatedDesc[1]()}
</Paragraph>
<Button
title="Open issue"
title={strings.openIssue()}
onPress={() => {
Linking.openURL(issueUrl.current);
}}
@@ -135,12 +135,8 @@ Logged in: ${user ? "yes" : "no"}`,
) : (
<>
<DialogHeader
title={issueTitle || "Report issue"}
paragraph={
issueTitle
? "We are sorry, it seems that the app crashed due to an error. You can submit a bug report below so we can fix this asap."
: "Let us know if you have faced any issue/bug while using Notesnook."
}
title={issueTitle || strings.issueTitle()}
paragraph={issueTitle ? strings.issueDesc() : strings.issueDesc2()}
/>
<Seperator half />
@@ -211,7 +207,7 @@ For example:
<Seperator />
<Button
onPress={onPress}
title={loading ? null : "Submit"}
title={loading ? null : strings.submit()}
loading={loading}
width="100%"
type="accent"
@@ -225,7 +221,7 @@ For example:
textAlign: "center"
}}
>
The information above will be publically available at{" "}
{strings.issueNotice[0]()}{" "}
<Text
onPress={() => {
Linking.openURL("https://github.com/streetwriters/notesnook");
@@ -237,8 +233,7 @@ For example:
>
github.com/streetwriters/notesnook.
</Text>{" "}
If you want to ask something in general or need some assistance, we
would suggest that you{" "}
{strings.issueNotice[1]()}{" "}
<Text
style={{
textDecorationLine: "underline",
@@ -255,7 +250,7 @@ For example:
}
}}
>
join our community on Discord.
{strings.issueNotice[2]()}
</Text>
</Paragraph>
</>

View File

@@ -39,6 +39,7 @@ import {
EditorEvents,
editorController
} from "../../../screens/editor/tiptap/utils";
import { strings } from "@notesnook/intl";
const ListNoteItem = ({
id,
@@ -117,7 +118,7 @@ const ListBlockItem = ({
{item?.content.length > 200
? item?.content.slice(0, 200) + "..."
: !item.content || item.content.trim() === ""
? "(Empty block)"
? strings.linkNoteEmptyBlock()
: item.content}
</Paragraph>
@@ -256,7 +257,7 @@ export default function LinkNote(props: {
}}
>
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
SELECTED NOTE
{strings.linkNoteSelectedNote()}
</Paragraph>
<Pressable
onPress={() => {
@@ -286,7 +287,7 @@ export default function LinkNote(props: {
<Paragraph numberOfLines={1}>{selectedNote?.title}</Paragraph>
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
Tap to deselect
{strings.tapToDeselect()}
</Paragraph>
</View>
</Pressable>
@@ -299,7 +300,7 @@ export default function LinkNote(props: {
color={colors.secondary.paragraph}
size={SIZE.xs}
>
LINK TO A SECTION
{strings.linkNoteToSection()}
</Paragraph>
) : null}
</View>
@@ -342,7 +343,7 @@ export default function LinkNote(props: {
style={{
marginTop: 10
}}
title="Create link"
title={strings.createLink()}
type="accent"
width="100%"
onPress={() => {

View File

@@ -48,6 +48,7 @@ import Input from "../../ui/input";
import { Pressable } from "../../ui/pressable";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations
@@ -296,7 +297,7 @@ const ManageTagsSheet = (props: {
type="selected"
>
<Heading size={SIZE.sm} color={colors.selected.heading}>
Add {'"' + "#" + query + '"'}
{strings.add()} {'"' + "#" + query + '"'}
</Heading>
<Icon name="plus" color={colors.selected.icon} size={SIZE.lg} />
</Pressable>
@@ -331,7 +332,7 @@ const ManageTagsSheet = (props: {
textBreakStrategy="balanced"
color={colors.secondary.paragraph}
>
You do not have any tags.
{strings.emptyPlaceholders("tag")}
</Paragraph>
</View>
}

View File

@@ -41,6 +41,7 @@ import Seperator from "../../ui/seperator";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import { Issue } from "../github/issue";
import { strings } from "@notesnook/intl";
export const makeError = (stack: string, component: string) => `
@@ -133,11 +134,9 @@ export default function Migrate() {
>
{!loading && !error ? (
<DialogHeader
title="Save a backup of your notes"
title={strings.migrationSaveBackup()}
centered
paragraph={
"Thank you for updating Notesnook! We will be applying some minor changess for a better note taking experience."
}
paragraph={strings.migrationSaveBackupDesc()}
/>
) : null}
<Seperator />
@@ -158,9 +157,7 @@ export default function Migrate() {
textAlign: "center"
}}
>
Migrating database{progress ? `(${progress?.collection})` : null}
{progress ? `(${progress.current}/${progress.total}) ` : null}
... please wait
{strings.migrationProgress(progress)}
</Paragraph>
<View
@@ -190,9 +187,7 @@ export default function Migrate() {
textAlign: "center"
}}
>
An error occurred while migrating your data. You can logout of your
account and try to relogin. However this is not recommended as it
may result in some data loss if your data was not synced.
{strings.migrationError()}
</Paragraph>
{reset ? (
@@ -202,11 +197,11 @@ export default function Migrate() {
textAlign: "center"
}}
>
App data has been cleared. Kindly relaunch the app to login again.
{strings.migrationAppReset()}
</Paragraph>
) : (
<Button
title="Logout & clear app data"
title={strings.logoutAnClearData()}
type="error"
width={250}
onPress={async () => {
@@ -224,7 +219,7 @@ export default function Migrate() {
</>
) : (
<Button
title="Save & continue"
title={strings.saveAndContinue()}
type="accent"
width={250}
onPress={startMigration}

View File

@@ -45,6 +45,7 @@ import { Pressable } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
import { strings } from "@notesnook/intl";
const useNotebookExpandedStore = create<{
expanded: {
@@ -152,12 +153,10 @@ export const MoveNotebookSheet = ({
}}
>
<DialogHeader
title={`Moving ${
selectedNotebooks.length > 1
? selectedNotebooks.length + " notebooks"
: selectedNotebooks[0].title
}`}
paragraph={"Select the notebook you want to move this notebook to."}
title={strings.moveNotebook(
selectedNotebooks.length,
selectedNotebooks[0].title
)}
/>
</View>
<Seperator />
@@ -174,7 +173,7 @@ export const MoveNotebookSheet = ({
>
{moveToTop ? (
<Button
title="Move to top"
title={strings.moveToTop()}
style={{
alignSelf: "flex-start",
width: "100%",

View File

@@ -39,6 +39,7 @@ import Input from "../../ui/input";
import { Pressable } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
const useItemSelectionStore = createItemSelectionStore(true);
@@ -123,10 +124,7 @@ export const MoveNotes = ({
>
<Dialog context="local" />
<DialogHeader
title={`Add notes to ${currentNotebook.title}`}
paragraph={"Select the notes you want to move to this Notebook."}
/>
<DialogHeader title={strings.addNotesToNotebook(currentNotebook.title)} />
<Seperator />
<Input
@@ -158,7 +156,7 @@ export const MoveNotes = ({
<ActivityIndicator size="large" color={colors.primary.accent} />
) : (
<Paragraph color={colors.secondary.paragraph}>
No notes to show
{strings.emptyPlaceholders("note")}
</Paragraph>
)}
</View>
@@ -185,7 +183,7 @@ export const MoveNotes = ({
Navigation.queueRoutesForUpdate();
fwdRef?.current?.hide();
}}
title="Move selected notes"
title={strings.moveSelectedNotes()}
type="accent"
width="100%"
/>

View File

@@ -31,6 +31,7 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export type FeatureType = {
title: string;
body: string;
@@ -56,7 +57,7 @@ const NewFeature = ({
}}
>
<Heading color={colors.secondary.heading} size={SIZE.md}>
{!version ? "New version " : `v${version} `}Highlights 🎉
{!version ? `${strings.newVersion()} ` : `v${version} `}Highlights 🎉
</Heading>
<Seperator />
@@ -81,7 +82,7 @@ const NewFeature = ({
<Seperator />
<Button
title="Got it"
title={strings.gotIt()}
type="accent"
width={250}
style={{

View File

@@ -51,6 +51,7 @@ import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
import { MoveNotebookSheet } from "../move-notebook";
import Sort from "../sort";
import { strings } from "@notesnook/intl";
const useItemSelectionStore = createItemSelectionStore(true, false);
@@ -289,7 +290,7 @@ export const NotebookSheet = () => {
}}
>
<Paragraph size={SIZE.xs} color={colors.primary.icon}>
NOTEBOOKS
{strings.notebooks()}
</Paragraph>
<View
style={{
@@ -433,7 +434,9 @@ export const NotebookSheet = () => {
height: 200
}}
>
<Paragraph color={colors.primary.icon}>No notebooks</Paragraph>
<Paragraph color={colors.primary.icon}>
{strings.emptyPlaceholders("notebook")}
</Paragraph>
</View>
}
/>

View File

@@ -27,6 +27,7 @@ import Seperator from "../../ui/seperator";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const Progress = () => {
const { colors } = useThemeColors();
const { progress } = useSyncProgress();
@@ -51,8 +52,8 @@ export const Progress = () => {
paddingBottom: 15
}}
>
<Heading size={SIZE.lg}>Syncing your data</Heading>
<Paragraph>Please wait while we sync all your data.</Paragraph>
<Heading size={SIZE.lg}>{strings.syncingHeading()}</Heading>
<Paragraph>{strings.syncingDesc()}</Paragraph>
<Seperator />
<View
style={{
@@ -73,8 +74,7 @@ export const Progress = () => {
{progress ? (
<Paragraph color={colors.secondary.paragraph}>
{progress.type?.slice(0, 1).toUpperCase() + progress.type?.slice(1)}
ing {progress?.current}
{strings.networkProgress(progress.type)} {progress?.current}
</Paragraph>
) : null}
</View>

View File

@@ -37,6 +37,7 @@ import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { requestInAppReview } from "../../../services/app-review";
import { Note } from "@notesnook/core";
import { strings } from "@notesnook/intl";
const PublishNoteSheet = ({
note: item
@@ -120,10 +121,8 @@ const PublishNoteSheet = ({
}}
>
<DialogHeader
title={note?.title}
paragraph={`Anyone with the link${
isLocked ? " and password" : ""
} of the published note can view it.`}
title={`${strings.publish()} ${strings.note()}`}
paragraph={strings.publishDesc()}
/>
{publishing ? (
@@ -141,9 +140,9 @@ const PublishNoteSheet = ({
textAlign: "center"
}}
>
Please wait...
{strings.pleaseWait()}...
{downloading && downloading.current && downloading.total
? `\nDownloading attachments (${
? `\n${strings.downloadingAttachments()} (${
downloading?.current / downloading?.total
})`
: ""}
@@ -168,7 +167,7 @@ const PublishNoteSheet = ({
flexShrink: 1
}}
>
<Heading size={SIZE.md}>Published at:</Heading>
<Heading size={SIZE.md}>{strings.publishedAt()}:</Heading>
<Paragraph size={SIZE.sm} numberOfLines={1}>
{publishUrl}
</Paragraph>
@@ -186,8 +185,8 @@ const PublishNoteSheet = ({
color: colors.primary.paragraph
}}
>
<Icon color={colors.primary.accent} name="open-in-new" /> Open
in browser
<Icon color={colors.primary.accent} name="open-in-new" />{" "}
{strings.openInBrowser()}
</Paragraph>
</View>
@@ -243,10 +242,8 @@ const PublishNoteSheet = ({
flexShrink: 1
}}
>
<Heading size={SIZE.md}>Password protection</Heading>
<Paragraph>
Published note can only be viewed by someone with the password.
</Paragraph>
<Heading size={SIZE.md}>{strings.monographPassHeading()}</Heading>
<Paragraph>{strings.monographPassDesc()}</Paragraph>
</View>
</TouchableOpacity>
@@ -282,11 +279,10 @@ const PublishNoteSheet = ({
flexShrink: 1
}}
>
<Heading size={SIZE.md}>Self destruct</Heading>
<Paragraph>
Published note link will be automatically deleted once it is
viewed by someone.
</Paragraph>
<Heading size={SIZE.md}>
{strings.monographSelfDestructHeading()}
</Heading>
<Paragraph>{strings.monographSelfDestructDesc()}</Paragraph>
</View>
</TouchableOpacity>
@@ -324,7 +320,7 @@ const PublishNoteSheet = ({
onPress={deletePublishedNote}
fontSize={SIZE.md}
type="error"
title="Unpublish"
title={strings.unpublish()}
style={{
width: "49%"
}}
@@ -340,7 +336,7 @@ const PublishNoteSheet = ({
borderRadius: isPublished ? 5 : 100
}}
type="accent"
title={isPublished ? "Update" : "Publish"}
title={isPublished ? strings.update() : strings.publish()}
/>
</View>
</View>
@@ -365,7 +361,7 @@ const PublishNoteSheet = ({
}
}}
>
Learn more about Notesnook Monograph
{strings.monographLearnMore()}
</Paragraph>
</View>
);

View File

@@ -33,6 +33,7 @@ import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
const RateAppSheet = () => {
const [visible, setVisible] = useState(false);
const actionSheetRef = useRef();
@@ -85,11 +86,8 @@ const RateAppSheet = () => {
paddingHorizontal: 12
}}
>
<Heading>Do you enjoy using Notesnook?</Heading>
<Paragraph size={SIZE.md}>
It took us a year to bring Notesnook to life. Share your experience
and suggestions to help us improve it.
</Paragraph>
<Heading>{strings.rateAppHeading()}</Heading>
<Paragraph size={SIZE.md}>{strings.rateAppDesc()}</Paragraph>
<Seperator half />
<Button
@@ -97,7 +95,7 @@ const RateAppSheet = () => {
fontSize={SIZE.md}
width="100%"
type="accent"
title="Rate now (It takes only a second)"
title={strings.rateApp()}
/>
<View
style={{
@@ -120,14 +118,14 @@ const RateAppSheet = () => {
fontSize={SIZE.md}
type="error"
width="48%"
title="Never"
title={strings.never()}
/>
<Button
onPress={onClose}
fontSize={SIZE.md}
width="48%"
type="secondary"
title="Later"
title={strings.later()}
/>
</View>
</View>

View File

@@ -43,6 +43,7 @@ import { QRCode } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import RNFetchBlob from "react-native-blob-util";
import { sanitizeFilename } from "@notesnook/common";
import { strings } from "@notesnook/intl";
class RecoveryKeySheet extends React.Component {
constructor(props) {
@@ -227,9 +228,8 @@ class RecoveryKeySheet extends React.Component {
}}
>
<DialogHeader
title="Save account recovery key"
paragraph="If you forget your password, you can recover your
data and reset your password only using this recovery key."
title={strings.saveRecoveryKey()}
paragraph={strings.saveRecoveryKeyDesc()}
/>
<View
@@ -290,14 +290,14 @@ class RecoveryKeySheet extends React.Component {
});
}}
icon="content-copy"
title="Copy to clipboard"
title={strings.copyToClipboard()}
width="100%"
type="secondaryAccented"
fontSize={SIZE.md}
/>
<Seperator />
<Button
title="Save QR-Code to gallery"
title={strings.saveQRCode()}
onPress={this.saveQRCODE}
width="100%"
type="secondaryAccented"
@@ -307,7 +307,7 @@ class RecoveryKeySheet extends React.Component {
<Seperator />
<Button
onPress={this.saveToTextFile}
title="Save to text file"
title={strings.saveAsText()}
width="100%"
type="secondaryAccented"
icon="text"
@@ -317,7 +317,7 @@ class RecoveryKeySheet extends React.Component {
<Button
onPress={this.shareFile}
title="Share to Cloud"
title={strings.shareToCloud()}
width="100%"
type="secondaryAccented"
icon="cloud"
@@ -336,10 +336,10 @@ class RecoveryKeySheet extends React.Component {
textAlign: "center"
}}
>
Tap twice to confirm you have saved the recovery key.
{strings.recoveryKeySavedConfirmation()}
</Paragraph>
<Button
title="I have saved the key."
title={strings.done()}
width="100%"
type="error"
fontSize={SIZE.md}

View File

@@ -41,6 +41,7 @@ import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import { Pressable } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const useExpandedStore = create<{
expanded: {
@@ -99,7 +100,7 @@ const ListBlockItem = ({
{item?.content.length > 200
? item?.content.slice(0, 200) + "..."
: !item.content || item.content.trim() === ""
? "(Empty block)"
? strings.linkNoteEmptyBlock()
: item.content}
</Paragraph>
@@ -360,7 +361,7 @@ const ListNoteItem = ({
<>
{linkedBlocks.length === 0 ? (
<Paragraph color={colors.secondary.paragraph}>
No blocks linked
{strings.noBlocksLinked()}
</Paragraph>
) : (
linkedBlocks.map(renderBlock)
@@ -370,7 +371,7 @@ const ListNoteItem = ({
<>
{noteInternalLinks.length === 0 ? (
<Paragraph color={colors.secondary.paragraph}>
No references found of this note
{strings.noReferencesFound()}
</Paragraph>
) : (
noteInternalLinks.map(renderInternalLink)
@@ -447,7 +448,7 @@ export const ReferencesList = ({ item, close }: ReferencesListProps) => {
>
<Button
type={"plain"}
title="Linked notes"
title={strings.linkedNotes()}
style={{
borderRadius: 0,
borderWidth: 0,
@@ -462,7 +463,7 @@ export const ReferencesList = ({ item, close }: ReferencesListProps) => {
/>
<Button
type={"plain"}
title="Referenced in"
title={strings.referencedIn()}
style={{
width: "50%",
borderWidth: 0,
@@ -486,9 +487,7 @@ export const ReferencesList = ({ item, close }: ReferencesListProps) => {
}}
>
<Paragraph color={colors.secondary.paragraph}>
{tab === 1
? "This note is not referenced in other notes."
: "This note does not link to other notes."}
{tab === 1 ? strings.notReferenced() : strings.notLinked()}
</Paragraph>
</View>
) : (

View File

@@ -37,6 +37,8 @@ import SheetProvider from "../../sheet-provider";
import { Button } from "../../ui/button";
import { PressableProps } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { isStringLiteralOrJsxExpression } from "typescript";
import { strings } from "@notesnook/intl";
type RelationsListProps = {
actionSheetRef: RefObject<ActionSheetRef>;
@@ -114,9 +116,7 @@ export const RelationsList = ({
size={60}
color={colors.primary.icon}
/>
<Paragraph>
No {referenceType}s linked to this {item.type}.
</Paragraph>
<Paragraph>{strings.noLinksFound()}</Paragraph>
<Button
onPress={() => {
onAdd?.();
@@ -125,7 +125,7 @@ export const RelationsList = ({
// width="100%"
type="inverted"
icon="plus"
title={`Add a ${referenceType}`}
title={strings.addItem(referenceType)}
/>
</View>
) : (

View File

@@ -40,6 +40,7 @@ import {
VirtualizedGrouping,
Note
} from "@notesnook/core";
import { strings } from "@notesnook/intl";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheetRef>;
@@ -134,7 +135,7 @@ export default function ReminderNotify({
marginTop: 10
}}
>
<Paragraph size={SIZE.xs}>Remind me in:</Paragraph>
<Paragraph size={SIZE.xs}>{strings.remindMeIn()}:</Paragraph>
{QuickActions.map((item) => {
return (
<Button
@@ -171,7 +172,7 @@ export default function ReminderNotify({
marginBottom: 10
}}
>
REFERENCED IN
{strings.referencedIn()}
</Paragraph>
<List
data={references}

View File

@@ -49,6 +49,7 @@ import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { ItemReference, Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheetRef>;
@@ -234,7 +235,7 @@ export default function ReminderSheet({
}}
>
<Heading size={SIZE.lg}>
{reminder ? "Edit reminder" : "New reminder"}
{reminder ? strings.editReminder() : strings.newReminder()}
</Heading>
<Dialog context="local" />
@@ -286,7 +287,9 @@ export default function ReminderSheet({
{Object.keys(ReminderModes).map((mode) => (
<Button
key={mode}
title={mode}
title={strings.reminderModes[
mode as keyof typeof strings.reminderModes
]()}
style={{
marginRight: 12,
borderRadius: 100,
@@ -342,9 +345,9 @@ export default function ReminderSheet({
{Object.keys(RecurringModes).map((mode) => (
<Button
key={mode}
title={
!repeatFrequency || repeatFrequency <= 1 ? mode : mode + "s"
}
title={strings.recurringModes[
mode as keyof typeof strings.recurringModes
]()}
style={{
marginRight: 6,
borderRadius: 100
@@ -376,10 +379,12 @@ export default function ReminderSheet({
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={WeekDayNames[index as keyof typeof WeekDayNames]}
title={WeekDayNames[
index as keyof typeof WeekDayNames
].slice(0, 1)}
key={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
title={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
type={
selectedDays.indexOf(index) > -1 ? "selected" : "plain"
}
@@ -478,7 +483,7 @@ export default function ReminderSheet({
style={{
width: "100%"
}}
title={date ? date.toLocaleDateString() : "Select date"}
title={date ? date.toLocaleDateString() : strings.selectDate()}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
fontSize={SIZE.md}
@@ -507,27 +512,28 @@ export default function ReminderSheet({
<>
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
{recurringMode === RecurringModes.Daily
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
? strings.reminderRepeatStrings.day(
dayjs(date).format("hh:mm A")
)
: recurringMode === RecurringModes.Year
? `The reminder will repeat every year on ${dayjs(
date
).format("dddd, MMMM D, h:mm A")}.`
? strings.reminderRepeatStrings.year(
dayjs(date).format("dddd, MMMM D, h:mm A")
)
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? `The reminder will repeat daily at ${dayjs(date).format(
"hh:mm A"
)}.`
? strings.reminderRepeatStrings.week.daily(
dayjs(date).format("hh:mm A")
)
: selectedDays.length === 0
? recurringMode === RecurringModes.Week
? "Select day of the week to repeat the reminder."
: "Select nth day of the month to repeat the reminder."
: `Repeats every${
repeatFrequency > 1 ? " " + repeatFrequency : ""
} ${
repeatFrequency > 1 ? recurringMode + "s" : recurringMode
} on ${getSelectedDaysText(selectedDays)} at ${dayjs(
date
).format("hh:mm A")}.`}
? strings.reminderRepeatStrings[
recurringMode as "week" | "month"
].selectDays()
: strings.reminderRepeatStrings.repeats(
repeatFrequency,
recurringMode as string,
getSelectedDaysText(selectedDays),
dayjs(date).format("hh:mm A")
)}
</Paragraph>
</>
</View>
@@ -556,7 +562,9 @@ export default function ReminderSheet({
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={mode}
title={strings.reminderNotificationModes[
mode as keyof typeof strings.reminderNotificationModes
]()}
style={{
marginRight: 12,
borderRadius: 100
@@ -593,7 +601,7 @@ export default function ReminderSheet({
</ScrollView>
<Button
title="Save"
title={strings.save()}
type="accent"
height={45}
fontSize={SIZE.md}

View File

@@ -29,6 +29,7 @@ import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import { strings } from "@notesnook/intl";
const Sort = ({ type, screen }) => {
const isTopicSheet = screen === "TopicSheet";
const { colors } = useThemeColors();
@@ -80,7 +81,7 @@ const Sort = ({ type, screen }) => {
alignSelf: "center"
}}
>
Sort by
{strings.sortBy()}
</Heading>
<Button
@@ -88,16 +89,16 @@ const Sort = ({ type, screen }) => {
groupOptions?.sortDirection === "asc"
? groupOptions?.groupBy === "abc" ||
groupOptions?.sortBy === "title"
? "A - Z"
? strings.aToZ()
: groupOptions?.sortBy === "dueDate"
? "Earliest first"
: "Old - New"
? strings.earliestFirst()
: strings.oldNew()
: groupOptions?.groupBy === "abc" ||
groupOptions?.sortBy === "title"
? "Z - A"
? strings.zToA()
: groupOptions?.sortBy === "dueDate"
? "Latest first"
: "New - Old"
? strings.latestFirst()
: strings.newOld()
}
icon={
groupOptions?.sortDirection === "asc"
@@ -137,7 +138,7 @@ const Sort = ({ type, screen }) => {
{groupOptions?.groupBy === "abc" ? (
<Button
type="secondary"
title="Title"
title={strings.title()}
height={40}
iconPosition="left"
icon={"check"}
@@ -155,7 +156,7 @@ const Sort = ({ type, screen }) => {
<Button
key={item}
type={groupOptions?.sortBy === item ? "selected" : "plain"}
title={SORT[item]}
title={strings.sortByStrings[item]()}
height={40}
iconPosition="left"
icon={groupOptions?.sortBy === item ? "check" : null}
@@ -195,7 +196,7 @@ const Sort = ({ type, screen }) => {
}}
size={SIZE.lg}
>
Group by
{strings.groupBy()}
</Heading>
<Seperator />
@@ -236,9 +237,7 @@ const Sort = ({ type, screen }) => {
}}
height={40}
icon={groupOptions?.groupBy === GROUP[item] ? "check" : null}
title={
item.slice(0, 1).toUpperCase() + item.slice(1, item.length)
}
title={strings.groupByStrings[item]()}
style={{
paddingHorizontal: 8,
marginBottom: 10,

View File

@@ -30,6 +30,7 @@ import {
editorState
} from "../../../screens/editor/tiptap/utils";
import { FlatList } from "react-native-actions-sheet";
import { strings } from "@notesnook/intl";
type TableOfContentsItem = {
level: number;
@@ -87,7 +88,7 @@ const TableOfContentsItem: React.FC<{
}
size={SIZE.md}
>
{item?.title || "New note"}
{item?.title || strings.newNote()}
</Paragraph>
</View>
</Pressable>
@@ -111,7 +112,7 @@ const TableOfContents = ({ toc, close }: TableOfContentsProps) => {
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>Table of contents</Heading>
<Heading size={SIZE.lg}>{strings.toc()}</Heading>
</View>
<FlatList

File diff suppressed because one or more lines are too long

View File

@@ -37,6 +37,7 @@ import { useSideBarDraggingStore } from "./dragging-store";
import { MenuItem } from "./menu-item";
import { PinnedSection } from "./pinned-section";
import { UserStatus } from "./user-status";
import { strings } from "@notesnook/intl";
export const SideMenu = React.memo(
function SideMenu() {
@@ -153,7 +154,7 @@ export const SideMenu = React.memo(
width: "100%"
}}
icon="close"
title="Tap to stop reordering"
title={strings.stopReordering()}
onPress={() => {
useSideBarDraggingStore.setState({
dragging: false

View File

@@ -29,6 +29,7 @@ import { Button } from "../ui/button";
import { Pressable } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
function _MenuItem({
item,
@@ -129,7 +130,7 @@ function _MenuItem({
}}
>
<Paragraph color={primaryColors.accentForeground} size={SIZE.xxs}>
BETA
{strings.beta()}
</Paragraph>
</View>
) : null}

View File

@@ -38,6 +38,7 @@ import Seperator from "../ui/seperator";
import SheetWrapper from "../ui/sheet";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const PinnedSection = React.memo(
function PinnedSection() {
@@ -100,7 +101,7 @@ export const PinnedSection = React.memo(
<Notice
size="small"
type="information"
text="Add shortcuts for notebooks and tags here."
text={strings.sideMenuNotice()}
style={{
marginHorizontal: 12
}}
@@ -152,7 +153,7 @@ export const PinItem = React.memo(
>
<Seperator />
<Button
title="Remove Shortcut"
title={strings.removeShortcut()}
type="error"
onPress={async () => {
await db.shortcuts.remove(item.id);

View File

@@ -36,6 +36,7 @@ import { IconButton } from "../ui/icon-button";
import { Pressable } from "../ui/pressable";
import { TimeSince } from "../ui/time-since";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const UserStatus = () => {
const { colors, isDark } = useThemeColors();
@@ -116,7 +117,7 @@ export const UserStatus = () => {
color={colors.primary.heading}
>
{!user || !userProfile?.fullName
? "Settings"
? strings.settings()
: userProfile.fullName}
</Paragraph>
@@ -128,14 +129,16 @@ export const UserStatus = () => {
color={colors.secondary.heading}
>
{!user ? (
"Not logged in"
strings.notLoggedIn()
) : lastSynced && lastSynced !== "Never" ? (
<>
{syncing
? `Syncing ${progress ? `(${progress.current})` : ""}`
? `${strings.syncing()} ${
progress ? `(${progress.current})` : ""
}`
: lastSyncStatus === SyncStatus.Failed
? "Sync failed"
: "Synced"}{" "}
? strings.syncFailed()
: strings.synced()}{" "}
{!syncing ? (
<TimeSince
style={{
@@ -145,10 +148,10 @@ export const UserStatus = () => {
time={lastSynced}
/>
) : null}
{isOffline ? " (offline)" : ""}
{isOffline ? ` (${strings.offline()})` : ""}
</>
) : (
"never"
strings.never()
)}{" "}
<Icon
name="checkbox-blank-circle"

View File

@@ -28,6 +28,7 @@ import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Seperator from "../ui/seperator";
import Paragraph from "../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
export const Tip = ({
tip,
@@ -67,7 +68,7 @@ export const Tip = ({
}}
>
<Button
title="TIP"
title={strings.tip()}
icon="information"
fontSize={SIZE.xs}
iconSize={SIZE.xs}
@@ -87,7 +88,7 @@ export const Tip = ({
{neverShowAgain && (
<Button
title="Never show again"
title={strings.neverShowAgain()}
type="secondary"
icon="close"
fontSize={SIZE.xs}

View File

@@ -29,6 +29,7 @@ import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import walkthroughs, { TStep } from "./walkthroughs";
import { strings } from "@notesnook/intl";
export const Walkthrough = ({
steps,
canSkip = true
@@ -122,7 +123,7 @@ export const Walkthrough = ({
eSendEvent(eCloseSheet);
}}
type="plain"
title="Skip introduction"
title={strings.skipIntroduction()}
/>
) : null}
</View>

View File

@@ -41,6 +41,7 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useThemeColors } from "@notesnook/theme";
import { getContainerBorder } from "../../utils/colors";
import { strings } from "@notesnook/intl";
export type TStep = {
text?: string;
@@ -109,7 +110,8 @@ const NotebookWelcome = () => {
size={SIZE.xs}
color={colors.secondary.paragraph}
>
Notebook - {data?.count} notes
{strings.dataTypesCamelCase.notebook()} - {data?.count}{" "}
{strings.dataTypesPlural.note()}
</Paragraph>
</View>
</View>

View File

@@ -41,7 +41,10 @@
"@trpc/react-query": "10.45.2",
"@trpc/server": "10.45.2",
"@streetwriters/kysely": "^0.27.4",
"pathe": "1.1.2"
"pathe": "1.1.2",
"react-native-format-currency": "0.0.5",
"@lingui/react": "4.11.2",
"@lingui/core": "4.11.2"
},
"sideEffects": false
}

View File

@@ -39,6 +39,7 @@ import { eClearEditor } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { useEditor } from "./tiptap/use-editor";
import { editorState } from "./tiptap/utils";
import { strings } from "@notesnook/intl";
const EditorOverlay = ({
editorId = "",
@@ -220,7 +221,7 @@ const EditorOverlay = ({
}}
>
<Paragraph color={colors.secondary.paragraph} size={13}>
Add a tag
{strings.addItem("tag")}
</Paragraph>
<IconButton
size={20}
@@ -325,7 +326,7 @@ const EditorOverlay = ({
editor.setLoading(true);
setTimeout(() => editor.setLoading(false), 10);
}}
title="Taking too long? Reload editor"
title={strings.reloadEditor()}
/>
<Paragraph
textBreakStrategy="balanced"
@@ -336,8 +337,7 @@ const EditorOverlay = ({
marginTop: 5
}}
>
If the editor fails to load even after reloading. Try restarting
the app.
{strings.editorFailedToLoad()}
</Paragraph>
</>
) : null}

View File

@@ -52,6 +52,7 @@ import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { sanitizeFilename } from "@notesnook/common";
import { strings } from "@notesnook/intl";
const mfaMethods: MFAMethod[] = [
{
id: "app",
@@ -105,8 +106,8 @@ export const MFAMethodsPickerStep = ({ recovery, onSuccess }: MFAStepProps) => {
return (
<>
<DialogHeader
title="Two-factor authentication"
paragraph="Protect your notes by enabling 2 factor authentication"
title={strings.twoFactorAuth()}
paragraph={strings.twoFactorAuthDesc()}
padding={12}
/>
<Seperator />
@@ -291,7 +292,9 @@ export const MFASetup = ({
height: 50
}}
/>
<Paragraph>Getting information.. please wait</Paragraph>
<Paragraph>
{strings.gettingInformation()}... {strings.pleaseWait()}
</Paragraph>
</View>
) : (
<>
@@ -327,11 +330,11 @@ export const MFASetup = ({
sending
? null
: method.id === "app"
? "Copy"
? strings.copy()
: `${
seconds
? `Resend code in (${seconds})`
: "Send code"
? strings.resendCode(seconds)
: strings.sendCode()
}`
}
/>
@@ -339,7 +342,7 @@ export const MFASetup = ({
}
/>
<Heading size={SIZE.md}>Enter the 6-digit code</Heading>
<Heading size={SIZE.md}>{strings.enterSixDigitCode()}</Heading>
<Paragraph>{codeHelpText[method?.id]}</Paragraph>
<Seperator />
<Input
@@ -364,7 +367,7 @@ export const MFASetup = ({
/>
<Seperator />
<Button
title={enabling ? null : "Next"}
title={enabling ? null : strings.next()}
type="accent"
width={250}
onPress={onNext}
@@ -376,7 +379,7 @@ export const MFASetup = ({
/>
<Button
title="Select a different 2FA method"
title="Change 2FA method"
type="plain"
height={25}
onPress={() => {
@@ -423,14 +426,8 @@ export const MFARecoveryCodes = ({
<View>
<DialogHeader
centered={true}
title="Save your recovery codes"
paragraph={`If you lose access to your ${
method?.id === "email"
? "email"
: method?.id === "sms"
? "phone"
: "auth app"
}, you can login to Notesnook using your recovery codes. Each code can only be used once.`}
title={strings.saveRecoveryCodes()}
paragraph={strings.saveRecoveryCodesDesc()}
padding={12}
/>
<Seperator />
@@ -450,7 +447,9 @@ export const MFARecoveryCodes = ({
height: 50
}}
/>
<Paragraph>Getting recovery codes.. please wait</Paragraph>
<Paragraph>
{strings.gettingRecoveryCodes()}... {strings.pleaseWait()}
</Paragraph>
</View>
) : (
<>
@@ -483,7 +482,7 @@ export const MFARecoveryCodes = ({
}}
>
<Button
title="Copy codes"
title={strings.copyCodes()}
fontSize={SIZE.md}
onPress={() => {
const codeString = codes.join("\n");
@@ -500,7 +499,7 @@ export const MFARecoveryCodes = ({
/>
<Button
title="Save to file"
title={strings.saveToFile()}
fontSize={SIZE.md}
onPress={async () => {
try {
@@ -542,7 +541,7 @@ export const MFARecoveryCodes = ({
</View>
<Button
title={isSetup ? "Next" : "Done"}
title={isSetup ? strings.next() : strings.done()}
type="accent"
width={250}
onPress={() => {
@@ -593,16 +592,16 @@ const MFASuccess = ({ recovery }: MFAStepProps) => {
centered={true}
title={
recovery
? "Fallback method for 2FA enabled"
: "Two-factor authentication enabled!"
? strings.fallbackMethodEnabled()
: strings.twoFactorAuthEnabled()
}
paragraph="Your account is now 100% secure against unauthorized logins."
paragraph={strings.accountIsSecure()}
padding={12}
/>
<Seperator />
<Button
title="Done"
title={strings.done()}
type="accent"
width={250}
onPress={() => {
@@ -616,7 +615,7 @@ const MFASuccess = ({ recovery }: MFAStepProps) => {
{!recovery ? (
<Button
title="Setup secondary 2FA method"
title={strings.secondary2faMethod()}
type="plain"
height={25}
onPress={() => {

View File

@@ -32,6 +32,7 @@ import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { SIZE } from "../../utils/size";
import { strings } from "@notesnook/intl";
const AppLock = () => {
const { colors } = useThemeColors();
const appLockMode = useSettingStore((state) => state.settings.appLockMode);
@@ -97,10 +98,8 @@ const AppLock = () => {
width: "100%"
}}
>
<Heading>Protect your notes</Heading>
<Paragraph size={SIZE.md}>
Choose how you want to secure your notes locally.
</Paragraph>
<Heading>{strings.protectNotes()}</Heading>
<Paragraph size={SIZE.md}>{strings.protectNotesDesc()}</Paragraph>
</View>
</View>

View File

@@ -34,6 +34,7 @@ import { ToastManager } from "../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { hexToRGBA } from "../../utils/colors";
import { sanitizeFilename } from "@notesnook/common";
import { strings } from "@notesnook/intl";
export default function DebugLogs() {
const { colors } = useThemeColors();
@@ -213,10 +214,7 @@ export default function DebugLogs() {
padding: 12
}}
>
<Notice
text="All logs are local only and are not sent to any server. You can share the logs from here with us if you face an issue to help us find the root cause."
type="information"
/>
<Notice text={strings.debugNotice()} type="information" />
</View>
{currentLog && (

View File

@@ -29,6 +29,7 @@ import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../../utils/size";
import { Group } from "./group";
import { DragState, useDragState } from "./state";
import { strings } from "@notesnook/intl";
export const ConfigureToolbar = () => {
const data = useDragState((state) => state.data);
const preset = useDragState((state) => state.preset);
@@ -52,10 +53,7 @@ export const ConfigureToolbar = () => {
paddingVertical: 12
}}
>
<Notice
text="Curate the toolbar that fits your needs and matches your personality."
type="information"
/>
<Notice text={strings.configureToolbarNotice()} type="information" />
<Paragraph
style={{
@@ -64,7 +62,7 @@ export const ConfigureToolbar = () => {
size={SIZE.xs}
color={colors.secondary.paragraph}
>
PRESETS
{strings.presets()}
</Paragraph>
<View
@@ -128,7 +126,7 @@ export const ConfigureToolbar = () => {
}}
>
<Button
title="Create a group"
title={strings.createAGroup()}
type="secondaryAccented"
icon="plus"
style={{

View File

@@ -34,6 +34,7 @@ import ToolSheet from "./tool-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import type { ToolId } from "@notesnook/editor";
import PremiumService from "../../../services/premium";
import { strings } from "@notesnook/intl";
export const Group = ({
item,
@@ -203,7 +204,7 @@ export const Group = ({
color={colors.secondary.paragraph}
size={SIZE.xs}
>
GROUP
{strings.group()}
</Paragraph>
</View>

View File

@@ -33,6 +33,7 @@ import {
getUngroupedTools
} from "./toolbar-definition";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import { strings } from "@notesnook/intl";
export default function ToolSheet({
group,
@@ -124,7 +125,7 @@ export default function ToolSheet({
}}
color={colors.secondary.paragraph}
>
You have grouped all the tools.
{strings.groupedAllTools()}
</Paragraph>
) : (
ungrouped.map(renderTool)

View File

@@ -36,6 +36,7 @@ import ToolSheet from "./tool-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import type { ToolId } from "@notesnook/editor";
import PremiumService from "../../../services/premium";
import { strings } from "@notesnook/intl";
export const Tool = ({
item,
@@ -221,7 +222,7 @@ export const Tool = ({
}
size={isSubgroup ? SIZE.xs : SIZE.sm - 1}
>
{isSubgroup ? "COLLAPSED" : tool?.title}
{isSubgroup ? strings.collapsed() : tool?.title}
</Paragraph>
</View>

View File

@@ -29,6 +29,8 @@ import { SectionGroup } from "./section-group";
import { settingsGroups } from "./settings-data";
import { RouteParams, SettingSection } from "./types";
import SettingsUserSection from "./user-section";
import { strings } from "@notesnook/intl";
const keyExtractor = (item: SettingSection) => item.id;
const Home = ({

View File

@@ -63,7 +63,7 @@ export const Licenses = () => {
</Paragraph>
</Pressable>
),
[colors.secondary.background]
[colors.primary.border]
);
return (
<FlatList

View File

@@ -35,6 +35,7 @@ import {
import { eOpenPremiumDialog } from "../../utils/events";
import { SIZE } from "../../utils/size";
import Config from "react-native-config";
import { strings } from "@notesnook/intl";
export const Subscription = () => {
const user = useUserStore((state) => state.user);
const monthlyPlan = usePricing("monthly");
@@ -46,7 +47,7 @@ export const Subscription = () => {
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED === user?.subscription?.type;
const subscriptionProviderInfo =
SUBSCRIPTION_PROVIDER[user?.subscription?.provider];
strings.subscriptionProviderInfo[user?.subscription?.provider];
const manageSubscription = () => {
if (!user?.isEmailConfirmed) {
@@ -110,17 +111,17 @@ export const Subscription = () => {
onPress={manageSubscription}
title={
!user?.isEmailConfirmed
? "Confirm your email"
? strings.confirmEmail()
: user.subscription?.provider === 3 && hasCancelledPremium
? "Manage subscription from desktop app"
? strings.manageSubDesktop()
: hasCancelledPremium &&
Platform.OS === "android" &&
Config.GITHUB_RELEASE !== "true"
? "Resubscribe from Google Playstore"
? strings.resubFromPlaystore()
: user.subscription?.type ===
SUBSCRIPTION_STATUS.PREMIUM_EXPIRED || hasCancelledPremium
? `Resubscribe to Pro (${getPrice() || "$4.49"} / mo)`
: `Get Pro (${getPrice() || "$4.49"} / mo)`
? `${strings.resubToPro()} (${getPrice() || "$4.49"} / mo)`
: `${strings.getPro()} (${getPrice() || "$4.49"} / mo)`
}
/>
) : null}

View File

@@ -55,6 +55,7 @@ import { MenuItemsList } from "../../utils/menu-items";
import { IconButton } from "../../components/ui/icon-button";
import { Pressable } from "../../components/ui/pressable";
import { getColorLinearShade } from "../../utils/colors";
import { strings } from "@notesnook/intl";
const THEME_SERVER_URL = "https://themes-api.notesnook.com";
//@ts-ignore
@@ -237,7 +238,7 @@ function ThemeSelector() {
color={colors.list.heading}
size={7}
>
Notes
{strings.dataTypesPluralCamelCase.note()}
</Heading>
</View>
@@ -275,7 +276,9 @@ function ThemeSelector() {
) : null}
<Button
title={item.colorScheme === "dark" ? "Dark" : "Light"}
title={
item.colorScheme === "dark" ? strings.dark() : strings.light()
}
type="secondaryAccented"
height={25}
buttonType={{
@@ -299,11 +302,8 @@ function ThemeSelector() {
<Heading size={SIZE.sm} color={themeColors.primary.heading}>
{item.name}
</Heading>
{/* <Paragraph color={themeColors.primary?.paragraph}>
{item.description}
</Paragraph> */}
<Paragraph size={SIZE.xs} color={themeColors.secondary?.paragraph}>
By {item.authors?.[0].name}
{strings.by()} {item.authors?.[0].name}
</Paragraph>
</TouchableOpacity>
</>
@@ -368,7 +368,7 @@ function ThemeSelector() {
type={
colorScheme === "" || !colorScheme ? "accent" : "secondary"
}
title="All"
title={strings.all()}
fontSize={SIZE.xs}
onPress={() => {
setColorScheme("");
@@ -378,7 +378,7 @@ function ThemeSelector() {
style={{ borderRadius: 100, minWidth: 60 }}
height={30}
type={colorScheme === "dark" ? "accent" : "secondary"}
title="Dark"
title={strings.dark()}
fontSize={SIZE.xs}
onPress={() => {
setColorScheme("dark");
@@ -389,7 +389,7 @@ function ThemeSelector() {
height={30}
fontSize={SIZE.xs}
type={colorScheme === "light" ? "accent" : "secondary"}
title="Light"
title={strings.light()}
onPress={() => {
setColorScheme("light");
}}
@@ -397,7 +397,7 @@ function ThemeSelector() {
</View>
<Button
title="Load from file"
title={strings.loadFromFile()}
style={{ borderRadius: 100, minWidth: 60 }}
height={30}
type={"secondaryAccented"}
@@ -446,10 +446,10 @@ function ThemeSelector() {
<ActivityIndicator color={colors.primary.accent} />
) : searchQuery ? (
<Paragraph color={colors.secondary.paragraph}>
No results found for {searchQuery}
{strings.noResultsForSearch(searchQuery)}
</Paragraph>
) : (
<Paragraph>No themes found.</Paragraph>
<Paragraph>{strings.noThemesFound()}.</Paragraph>
)}
</View>
}
@@ -464,7 +464,7 @@ function ThemeSelector() {
}}
>
<Paragraph color={colors.error.paragraph}>
Error loading themes. {themes.error.message}.
{strings.errorLoadingThemes()}. {themes.error.message}.
</Paragraph>
</View>
) : null
@@ -682,7 +682,7 @@ const ThemeSetter = ({
color={colors?.list.heading}
size={7}
>
Notes
{strings.dataTypesPluralCamelCase.note()}
</Heading>
</View>
@@ -703,7 +703,7 @@ const ThemeSetter = ({
size={SIZE.xs}
color={themeColors.colors.secondary.paragraph}
>
By {theme.authors?.[0]?.name}
{strings.by()} {theme.authors?.[0]?.name}
</Paragraph>
<View
style={{
@@ -716,7 +716,7 @@ const ThemeSetter = ({
size={SIZE.xs}
color={themeColors.colors.secondary.paragraph}
>
Version {theme.version}
${strings.version()} {theme.version}
</Paragraph>
<Paragraph
@@ -739,7 +739,7 @@ const ThemeSetter = ({
Linking.openURL(theme.homepage as string);
}}
>
Visit homepage
{strings.visitHomePage()}
</Paragraph>
</View>
) : null}
@@ -756,10 +756,10 @@ const ThemeSetter = ({
>
<Heading color={colors.accentForeground} size={SIZE.md}>
{darkTheme.id === theme.id
? "Applied as dark theme"
: "Applied as light theme"}
? strings.appliedDark()
: strings.appliedLight()}
</Heading>
<Paragraph size={SIZE.xs}>(Tap to apply again)</Paragraph>
<Paragraph size={SIZE.xs}>({strings.tapToApplyAgain()})</Paragraph>
</Pressable>
) : (
<Button
@@ -770,8 +770,8 @@ const ThemeSetter = ({
onPress={applyTheme}
title={
theme.colorScheme === "dark"
? "Set as dark theme"
: "Set as light theme"
? strings.setAsDarkTheme()
: strings.setAsLightTheme()
}
type="secondaryAccented"
/>

View File

@@ -24,6 +24,7 @@ import { TextInput } from "react-native";
import Paragraph from "../../components/ui/typography/paragraph";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { strings } from "@notesnook/intl";
export const TitleFormat = () => {
const [titleFormat] = useState(db.settings.getTitleFormat());
@@ -53,14 +54,7 @@ export const TitleFormat = () => {
color={colors.secondary.paragraph}
size={SIZE.xs}
>
Use the following key to format the title:{"\n"}
{"\n"}
$date$: Current date.{"\n"}
$time$: Current time.{"\n"}
$timestamp$: Full date and time without any spaces or other symbols.
(e.g 202305261253).{"\n"}
$count$: Number of notes + 1.{"\n"}
$headline$: Use starting line of the note as title.{"\n"}
{strings.titleFormattingGuide()}
</Paragraph>
</>
);

View File

@@ -35,6 +35,7 @@ import { SyncStatus, useUserStore } from "../../stores/use-user-store";
import { SUBSCRIPTION_STATUS_STRINGS } from "../../utils/constants";
import { SIZE } from "../../utils/size";
import { SectionItem } from "./section-item";
import { strings } from "@notesnook/intl";
export const getTimeLeft = (t2) => {
let daysRemaining = dayjs(t2).diff(dayjs(), "days");
@@ -176,7 +177,7 @@ const SettingsUserSection = ({ item }) => {
<Heading color={colors.primary.accent} size={SIZE.sm}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}
]?.toUpperCase() || strings.basic()}
</Heading>
) : null}
@@ -208,7 +209,7 @@ const SettingsUserSection = ({ item }) => {
>
{userProfile?.fullName
? userProfile.fullName + " "
: "Set your name "}
: strings.setYourName() + " "}
<AppIcon name="pencil" size={SIZE.lg} />
</Paragraph>
@@ -225,12 +226,12 @@ const SettingsUserSection = ({ item }) => {
color={colors.secondary.heading}
>
{!user ? (
"Not logged in"
strings.notLoggedIn()
) : lastSynced && lastSynced !== "Never" ? (
<>
{lastSyncStatus === SyncStatus.Failed
? "Sync failed"
: "Synced"}{" "}
? strings.syncFailed()
: strings.synced()}{" "}
<TimeSince
style={{
fontSize: SIZE.xs,
@@ -238,10 +239,10 @@ const SettingsUserSection = ({ item }) => {
}}
time={lastSynced}
/>
{isOffline ? " (offline)" : ""}
{isOffline ? ` (${strings.offline()})` : ""}
</>
) : (
"never"
strings.never()
)}{" "}
<Icon
name="checkbox-blank-circle"

View File

@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { strings } from "@notesnook/intl";
import isEmail from "validator/lib/isEmail";
export function validateEmail(email) {
@@ -28,7 +29,7 @@ export function validateEmail(email) {
}
export const ERRORS_LIST = {
SHORT_PASS: "Atleast 8 characters"
SHORT_PASS: strings.passTooShort()
};
export function validatePass(password) {

View File

@@ -1 +0,0 @@

View File

@@ -1,8 +1,12 @@
const env = process.env.BABEL_ENV || process.env.NODE_ENV;
const configs = {
env: {
development: {
presets: ['module:@react-native/babel-preset'],
let PROCESS_ENV = process.env.BABEL_ENV || process.env.NODE_ENV;
/**
* @type {import('@babel/core').ConfigFunction}
*/
module.exports = function (api, opts) {
if (!PROCESS_ENV) PROCESS_ENV = 'production';
const env = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'@babel/plugin-transform-named-capturing-groups-regex',
'react-native-reanimated/plugin',
@@ -14,52 +18,23 @@ const configs = {
plugins: [
["@babel/plugin-transform-private-methods", { "loose": true }]
]
},
]
}
]
},
test: {
presets: ['module:@react-native/babel-preset'],
plugins: [
'@babel/plugin-transform-named-capturing-groups-regex',
'react-native-reanimated/plugin',
],
overrides: [
{
test: '../node_modules/kysely',
plugins: [
["@babel/plugin-transform-private-methods", { "loose": true }]
]
},
{
if (env === 'production') {
env.plugins.push('transform-remove-console');
}
if (env === 'test') {
env.overrides.push({
test: '../node_modules/jest-runner',
plugins: [
["@babel/plugin-transform-private-methods", { "loose": true }]
]
})
}
]
},
production: {
presets: ['module:@react-native/babel-preset'],
plugins: [
'transform-remove-console',
'@babel/plugin-transform-named-capturing-groups-regex',
'react-native-reanimated/plugin',
"@babel/plugin-transform-export-namespace-from",
],
overrides: [
{
test: '../node_modules/kysely',
plugins: [
["@babel/plugin-transform-private-methods", { "loose": true }]
]
}
]
}
}
};
module.exports = function (api, opts) {
api.cache(true);
if (!env) env = 'production';
return configs.env[env];
return env;
};

View File

@@ -1,8 +1,23 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import "@azure/core-asynciterator-polyfill";
import '@formatjs/intl-locale/polyfill'
import '@formatjs/intl-pluralrules/polyfill'
import '@formatjs/intl-pluralrules/locale-data/en'
import '@formatjs/intl-pluralrules/locale-data/cs'
import '@formatjs/intl-pluralrules/locale-data/fr'
import 'react-native-url-polyfill/auto';
import "./polyfills/console-time.js"
global.Buffer = require('buffer').Buffer;
import '../app/common/logger/index';
import { DOMParser } from './worker.js';
global.DOMParser = DOMParser;
import { $en, setI18nGlobal } from "@notesnook/intl";
import { i18n } from "@lingui/core";
i18n.load({
en: $en,
});
i18n.activate("en");
setI18nGlobal(i18n);

View File

@@ -0,0 +1,27 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** @type {import('@lingui/conf').LinguiConfig} */
module.exports = {
locales: ["en", "cs", "fr"],
sourceLocale: "en",
format: "po",
compileNamespace: "ts"
};

View File

@@ -29,7 +29,6 @@ mergedConfig.resolver = {
"react-dom": path.join(__dirname, "../node_modules/react-dom"),
"@notesnook": path.join(__dirname, "../../../packages"),
"@notifee/react-native": path.join(__dirname, "../node_modules/@ammarahmed/notifee-react-native"),
},
resolveRequest: (context, moduleName, platform) => {
if (moduleName === "node:crypto") {
@@ -60,7 +59,7 @@ mergedConfig.resolver = {
};
}
return context.resolveRequest(context, moduleName, platform);
}
},
};
module.exports = mergedConfig;

View File

@@ -71,7 +71,9 @@
"@ammarahmed/react-native-background-fetch": "^4.2.2",
"react-native-image-crop-picker": "^0.40.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-screenguard": "^1.0.0"
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14"
},
"devDependencies": {
"@babel/core": "^7.20.0",

View File

@@ -104,6 +104,7 @@ module.exports = (env) => {
"@mdi/js": path.join(__dirname, "../node_modules/@mdi/js/mdi.js"),
"katex": path.join(__dirname, "../node_modules/katex"),
"tinycolor2": path.join(__dirname, "../node_modules/tinycolor2"),
"@lingui/core": path.join(__dirname, "../node_modules/@lingui/core"),
},
fallback: {
"crypto": false,
@@ -177,7 +178,7 @@ module.exports = (env) => {
plugins: [
"react-native-reanimated/plugin",
"@babel/plugin-transform-named-capturing-groups-regex",
["@babel/plugin-transform-private-methods", { "loose": true }]
["@babel/plugin-transform-private-methods", { "loose": true }],
]
},
},
@@ -222,6 +223,8 @@ module.exports = (env) => {
/node_modules(.*[/\\])+react-native-url-polyfill/,
/node_modules(.*[/\\])+diffblazer/,
/node_modules(.*[/\\])+react-freeze/,
/node_modules(.*[/\\])+@messageformat[/\\]parser/,
/node_modules(.*[/\\])+@lingui[/\\]core/,
],
use: {
loader: "babel-loader",
@@ -236,6 +239,7 @@ module.exports = (env) => {
plugins: [
"react-native-reanimated/plugin",
"@babel/plugin-transform-named-capturing-groups-regex",
"macros"
]
},
},
@@ -266,11 +270,13 @@ module.exports = (env) => {
? [
"module:react-refresh/babel",
"react-native-reanimated/plugin",
"macros"
]
: [
"react-native-reanimated/plugin",
`@babel/plugin-transform-named-capturing-groups-regex`,
"transform-remove-console",
"macros"
],
},
},

File diff suppressed because it is too large Load Diff

View File

@@ -42,6 +42,7 @@
"@notesnook/logger": "file:../../packages/logger",
"@notesnook/theme": "file:../../packages/theme",
"@notesnook/themes-server": "file:../../servers/themes",
"@notesnook/intl": "file:../../packages/intl",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5",

View File

@@ -18,6 +18,8 @@
"@emotion/react": "11.11.1",
"@hazae41/foras": "^2.1.4",
"@henrygd/queue": "^1.0.6",
"@lingui/core": "4.11.2",
"@lingui/react": "4.11.2",
"@mdi/js": "^7.2.96",
"@mdi/react": "^1.6.1",
"@notesnook-importer/core": "^2.1.1",
@@ -26,6 +28,7 @@
"@notesnook/crypto": "file:../../packages/crypto",
"@notesnook/desktop": "file:../desktop",
"@notesnook/editor": "file:../../packages/editor",
"@notesnook/intl": "file:../../packages/intl",
"@notesnook/logger": "file:../../packages/logger",
"@notesnook/streamable-fs": "file:../../packages/streamable-fs",
"@notesnook/theme": "file:../../packages/theme",
@@ -83,6 +86,7 @@
"react-virtuoso": "^4.6.2",
"timeago.js": "4.0.2",
"tinycolor2": "^1.6.0",
"vite-plugin-commonjs": "^0.10.1",
"w3c-keyname": "^2.2.6",
"web-streams-polyfill": "^3.1.1",
"wouter": "2.12.1",
@@ -33078,6 +33082,27 @@
}
}
},
"../../packages/intl": {
"name": "@notesnook/intl",
"version": "1.0.0",
"devDependencies": {
"@babel/core": "^7.24.9",
"@babel/preset-env": "^7.20.2",
"@lingui/cli": "^4.11.2",
"@lingui/macro": "^4.11.2",
"@lingui/swc-plugin": "^4.0.7",
"@rspack/cli": "^0.6.2",
"@rspack/core": "^0.6.2",
"@types/react": "^18.2.39",
"babel-plugin-macros": "^3.1.0",
"react": "18.2.0",
"typescript": "5.5.3"
},
"peerDependencies": {
"@lingui/macro": "*",
"react": ">=18"
}
},
"../../packages/logger": {
"name": "@notesnook/logger",
"version": "2.1.3",
@@ -42895,6 +42920,46 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lingui/core": {
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.11.2.tgz",
"integrity": "sha512-5wFmpHeDbLXEqaEUwlayS4SoqrCbDI3/bVRlwhmdNCeUcUYWh+7dTDlQnp4tPek1x1dEppABIkdN/0qLDdKcBQ==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@lingui/message-utils": "4.11.2",
"unraw": "^3.0.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@lingui/message-utils": {
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.11.2.tgz",
"integrity": "sha512-3oJk7ZKExk4NVa4d3CM0z0iNqIokaFOWeu7lYVzu0oEX7DP6OxNjlCAtObIhJCB0FdIPz8sXxhDkyDHFj+eIvw==",
"dependencies": {
"@messageformat/parser": "^5.0.0",
"js-sha256": "^0.10.1"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@lingui/react": {
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.11.2.tgz",
"integrity": "sha512-OKHCg3yPW2xhYWoY2kOz+eP7qpdkab+4tERUvJ9QJ9bzQ6OnPLCagaRftB3nqdKuWzKoA5F2VG2QLUhF7DjpGA==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@lingui/core": "4.11.2"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"license": "BSD-3-Clause",
@@ -42955,6 +43020,14 @@
"prop-types": "^15.7.2"
}
},
"node_modules/@messageformat/parser": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz",
"integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==",
"dependencies": {
"moo": "^0.5.1"
}
},
"node_modules/@microsoft/microsoft-graph-client": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz",
@@ -42983,7 +43056,6 @@
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@@ -42995,7 +43067,6 @@
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -43003,7 +43074,6 @@
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@@ -43140,6 +43210,10 @@
"resolved": "../../packages/editor",
"link": true
},
"node_modules/@notesnook/intl": {
"resolved": "../../packages/intl",
"link": true
},
"node_modules/@notesnook/logger": {
"resolved": "../../packages/logger",
"link": true
@@ -44389,6 +44463,28 @@
"@types/ms": "*"
}
},
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dev": true,
"peer": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dev": true,
"peer": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"dev": true,
@@ -44853,7 +44949,8 @@
},
"node_modules/acorn": {
"version": "8.11.3",
"dev": true,
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -45258,7 +45355,6 @@
},
"node_modules/braces": {
"version": "3.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.0.1"
@@ -46568,7 +46664,6 @@
},
"node_modules/fast-glob": {
"version": "3.3.2",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@@ -46588,7 +46683,6 @@
},
"node_modules/fastq": {
"version": "1.15.0",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -46674,7 +46768,6 @@
},
"node_modules/fill-range": {
"version": "7.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -46987,7 +47080,6 @@
},
"node_modules/glob-parent": {
"version": "5.1.2",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
@@ -47667,7 +47759,6 @@
},
"node_modules/is-extglob": {
"version": "2.1.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -47683,7 +47774,6 @@
},
"node_modules/is-glob": {
"version": "4.0.3",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -47710,7 +47800,6 @@
},
"node_modules/is-number": {
"version": "7.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -48304,7 +48393,8 @@
},
"node_modules/magic-string": {
"version": "0.30.10",
"dev": true,
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@@ -48614,7 +48704,6 @@
},
"node_modules/merge2": {
"version": "1.4.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -49146,7 +49235,6 @@
},
"node_modules/micromatch": {
"version": "4.0.5",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.2",
@@ -49275,6 +49363,11 @@
"ufo": "^1.5.3"
}
},
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
},
"node_modules/mri": {
"version": "1.2.0",
"license": "MIT",
@@ -49706,7 +49799,6 @@
},
"node_modules/picomatch": {
"version": "2.3.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
@@ -49952,7 +50044,6 @@
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"dev": true,
"funding": [
{
"type": "github",
@@ -50451,7 +50542,6 @@
},
"node_modules/reusify": {
"version": "1.0.4",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -50566,7 +50656,6 @@
},
"node_modules/run-parallel": {
"version": "1.2.0",
"dev": true,
"funding": [
{
"type": "github",
@@ -51374,7 +51463,6 @@
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -51902,6 +51990,28 @@
"url": "https://opencollective.com/vitest"
}
},
"node_modules/vite-plugin-commonjs": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.1.tgz",
"integrity": "sha512-taP8R9kYGlCW5OzkVR0UIWRCnG6rSxeWWuA7tnU5b9t5MniibOnDY219NhisTeDhJAeGT8cEnrhVWZ9A5yD+vg==",
"dependencies": {
"acorn": "^8.8.2",
"fast-glob": "^3.2.12",
"magic-string": "^0.30.1",
"vite-plugin-dynamic-import": "^1.5.0"
}
},
"node_modules/vite-plugin-dynamic-import": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.5.0.tgz",
"integrity": "sha512-Qp85c+AVJmLa8MLni74U4BDiWpUeFNx7NJqbGZyR2XJOU7mgW0cb7nwlAMucFyM4arEd92Nfxp4j44xPi6Fu7g==",
"dependencies": {
"acorn": "^8.8.2",
"es-module-lexer": "^1.2.1",
"fast-glob": "^3.2.12",
"magic-string": "^0.30.1"
}
},
"node_modules/vite-plugin-env-compatible": {
"version": "2.0.1",
"dev": true,

View File

@@ -16,6 +16,8 @@
"@emotion/react": "11.11.1",
"@hazae41/foras": "^2.1.4",
"@henrygd/queue": "^1.0.6",
"@lingui/core": "4.11.2",
"@lingui/react": "4.11.2",
"@mdi/js": "^7.2.96",
"@mdi/react": "^1.6.1",
"@notesnook-importer/core": "^2.1.1",
@@ -24,6 +26,7 @@
"@notesnook/crypto": "file:../../packages/crypto",
"@notesnook/desktop": "file:../desktop",
"@notesnook/editor": "file:../../packages/editor",
"@notesnook/intl": "file:../../packages/intl",
"@notesnook/logger": "file:../../packages/logger",
"@notesnook/streamable-fs": "file:../../packages/streamable-fs",
"@notesnook/theme": "file:../../packages/theme",
@@ -81,6 +84,7 @@
"react-virtuoso": "^4.6.2",
"timeago.js": "4.0.2",
"tinycolor2": "^1.6.0",
"vite-plugin-commonjs": "^0.10.1",
"w3c-keyname": "^2.2.6",
"web-streams-polyfill": "^3.1.1",
"wouter": "2.12.1",

View File

@@ -25,6 +25,13 @@ import { getServiceWorkerVersion } from "./utils/version";
import { register as registerStreamSaver } from "./utils/stream-saver/mitm";
import { ThemeDark, ThemeLight, themeToCSS } from "@notesnook/theme";
import Config from "./utils/config";
import Intl from "@notesnook/intl";
import { i18n } from "@lingui/core";
i18n.load({
en: Intl.$en
});
i18n.activate("en");
Intl.setI18nGlobal(i18n);
const colorScheme = JSON.parse(
window.localStorage.getItem("colorScheme") || '"light"'

Some files were not shown because too many files have changed in this diff Show More