Utilize nested locale translations and update usage
This commit is contained in:
parent
3c40673774
commit
cd4e628e11
|
@ -25,11 +25,11 @@ export const appUtilities = (app) => {
|
|||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.error !== false) {
|
||||
console.warn(response);
|
||||
console.warn(appState.i18n.__(response.message));
|
||||
return false;
|
||||
}
|
||||
|
||||
console.info(response.message);
|
||||
console.info(appState.i18n.__(response.message));
|
||||
appState.isLoggedIn = true;
|
||||
return true;
|
||||
});
|
||||
|
|
|
@ -5,12 +5,12 @@ export const loggedInView = (homeController, emit) => {
|
|||
|
||||
return [
|
||||
html`<section>
|
||||
<h2>${__('home.logged_in_subtitle')}</h2>
|
||||
<h2>${__('home.logged_in.subtitle')}</h2>
|
||||
<div class="flex one two-700">
|
||||
<div>
|
||||
<div class="card">
|
||||
<header>
|
||||
<h3>${__('home.logged_in_updates')}</h3>
|
||||
<h3>${__('home.logged_in.updates')}</h3>
|
||||
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||
<i class="icon-reload"></i>
|
||||
</button>
|
||||
|
@ -23,7 +23,7 @@ export const loggedInView = (homeController, emit) => {
|
|||
<div>
|
||||
<div class="card">
|
||||
<header>
|
||||
<h3>${__('home.logged_in_interactions')}</h3>
|
||||
<h3>${__('home.logged_in.interactions')}</h3>
|
||||
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||
<i class="icon-reload"></i>
|
||||
</button>
|
||||
|
|
|
@ -5,7 +5,7 @@ export const loggedOutView = (homeController, emit) => {
|
|||
|
||||
return [
|
||||
html`<section>
|
||||
<h2>${__('home.logged_out_subtitle')}</h2>
|
||||
<h2>${__('home.logged_out.subtitle')}</h2>
|
||||
<article class="flex one three-500">
|
||||
<div>
|
||||
<div class="card">
|
||||
|
@ -15,7 +15,7 @@ export const loggedOutView = (homeController, emit) => {
|
|||
</span>
|
||||
</header>
|
||||
<footer>
|
||||
${__('home.logged_out_track_books')}
|
||||
${__('home.logged_out.track_books')}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ export const loggedOutView = (homeController, emit) => {
|
|||
</span>
|
||||
</header>
|
||||
<footer>
|
||||
${__('home.logged_out_share_friends')}
|
||||
${__('home.logged_out.share_friends')}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,19 +39,19 @@ export const loggedOutView = (homeController, emit) => {
|
|||
</span>
|
||||
</header>
|
||||
<footer>
|
||||
${__('home.logged_out_read_rate')}
|
||||
${__('home.logged_out.read_rate')}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>`,
|
||||
html`<section>
|
||||
<h2>${__('home.logged_out_community_header')}</h2>
|
||||
<h2>${__('home.logged_out.community_header')}</h2>
|
||||
<div class="flex one two-700">
|
||||
<div>
|
||||
<div class="card">
|
||||
<header>
|
||||
<h3>${__('home.logged_out_recent_reviews')}</h3>
|
||||
<h3>${__('home.logged_out.recent_reviews')}</h3>
|
||||
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||
<i class="icon-reload"></i>
|
||||
</button>
|
||||
|
@ -64,7 +64,7 @@ export const loggedOutView = (homeController, emit) => {
|
|||
<div>
|
||||
<div class="card">
|
||||
<header>
|
||||
<h3>${__('home.logged_out_recent_updates')}</h3>
|
||||
<h3>${__('home.logged_out.recent_updates')}</h3>
|
||||
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||
<i class="icon-reload"></i>
|
||||
</button>
|
||||
|
@ -77,7 +77,7 @@ export const loggedOutView = (homeController, emit) => {
|
|||
</div>
|
||||
</section>`,
|
||||
html`<section class="center-align">
|
||||
<a href="/login" class="large success button">${__('home.logged_out_join_now')}</a>
|
||||
<a href="/login" class="large success button">${__('home.logged_out.join_now')}</a>
|
||||
</section>`,
|
||||
];
|
||||
}
|
|
@ -20,7 +20,7 @@ export const loginView = (state, emit, i18n) => {
|
|||
<header>
|
||||
${
|
||||
controller.state.loginMessage === ''
|
||||
? __('login.already_logged_in')
|
||||
? __('api.already_logged_in')
|
||||
: controller.state.loginMessage
|
||||
}
|
||||
</header>
|
||||
|
|
|
@ -48,7 +48,7 @@ export const searchView = (state, emit, i18n) => {
|
|||
<div>
|
||||
${modal('searchSourceInfo', controller, [
|
||||
html`<p>
|
||||
${__('search.search_source_help_text')}
|
||||
${__('search.search_source.help.text')}
|
||||
</p>`,
|
||||
html`<ul>
|
||||
<li>
|
||||
|
@ -57,7 +57,7 @@ export const searchView = (state, emit, i18n) => {
|
|||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
${__('search.search_source_help_inventaire')}
|
||||
${__('search.search_source.help.inventaire')}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -67,18 +67,18 @@ export const searchView = (state, emit, i18n) => {
|
|||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
${__('search.search_source_help_openLibrary')}
|
||||
${__('search.search_source.help.openLibrary')}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>`,
|
||||
], {
|
||||
buttonText: __('search.search_source_help_button'),
|
||||
buttonText: __('search.search_source.help.button'),
|
||||
buttonClasses: 'small marginless pseudo button pull-right',
|
||||
headerText: __('search.search_source_help_header'),
|
||||
headerText: __('search.search_source.help.header'),
|
||||
})}
|
||||
<label>
|
||||
${__('search.search_source_label')}
|
||||
${__('search.search_source.label')}
|
||||
|
||||
<select onchange=${event => {
|
||||
controller.state.searchSource = event.target.value;
|
||||
|
@ -93,7 +93,7 @@ export const searchView = (state, emit, i18n) => {
|
|||
</label>
|
||||
</div>
|
||||
<div>
|
||||
${__('search.search_by_label')}<br>
|
||||
${__('search.search_by.label')}<br>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="searchBy" value="title"
|
||||
|
@ -105,7 +105,7 @@ export const searchView = (state, emit, i18n) => {
|
|||
}}
|
||||
>
|
||||
<span class="checkable">
|
||||
${__('search.search_by_title')}
|
||||
${__('search.search_by.title')}
|
||||
</span>
|
||||
</label>
|
||||
<label>
|
||||
|
@ -118,7 +118,7 @@ export const searchView = (state, emit, i18n) => {
|
|||
}}
|
||||
>
|
||||
<span class="checkable">
|
||||
${__('search.search_by_author')}
|
||||
${__('search.search_by.author')}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -30,19 +30,19 @@ class AccountController {
|
|||
|| createAccountData.password === '') {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_create_required_data_missing',
|
||||
message: 'api.account.create.required_data_missing',
|
||||
};
|
||||
}
|
||||
if (createAccountData.email.length < 5 || !/^.+@.+\..+$/.test(createAccountData.email)) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_create_invalid_email',
|
||||
message: 'api.account.create.invalid_email',
|
||||
};
|
||||
}
|
||||
if (createAccountData.username.length < 2 || !/^[a-z0-9_]+$/i.test(createAccountData.username)) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_create_invalid_username',
|
||||
message: 'api.account.create.invalid_username',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,13 @@ class AccountController {
|
|||
|| loginData.password === '') {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_required_data_missing',
|
||||
message: 'api.account.login.required_data_missing',
|
||||
};
|
||||
}
|
||||
if (loginData.email.length < 5 || !/^.+@.+\..+$/.test(loginData.email)) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_email',
|
||||
message: 'api.account.login.invalid_email',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ class AccountController {
|
|||
|| !createAccountData.confirm) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_confirm_required_data_missing',
|
||||
message: 'api.account.confirm.required_data_missing',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -117,13 +117,13 @@ class AccountController {
|
|||
if (emailExists) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_email_exists',
|
||||
message: 'api.account.email_exists',
|
||||
};
|
||||
}
|
||||
if (usernameExists) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_username_exists',
|
||||
message: 'api.account.username_exists',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ class AccountController {
|
|||
if (!userToConfirm) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_confirm_invalid_code',
|
||||
message: 'api.account.confirm.invalid_code',
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ class AccountController {
|
|||
if (success[0] < 1) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_confirm_update_fail',
|
||||
message: 'api.account.confirm.update_fail',
|
||||
}
|
||||
}
|
||||
return userToConfirm;
|
||||
|
@ -192,14 +192,14 @@ class AccountController {
|
|||
if (existingUser == null) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_email',
|
||||
message: 'api.account.login.invalid_email',
|
||||
};
|
||||
}
|
||||
|
||||
if (existingUser.accountConfirm !== null) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_not_confirmed',
|
||||
message: 'api.account.login.not_confirmed',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ class AccountController {
|
|||
if (!passwordIsValid) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_password',
|
||||
message: 'api.account.login.invalid_password',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,20 +9,24 @@
|
|||
"menu_logout": "Log Out",
|
||||
"footer_repo": "Repo",
|
||||
"footer_chat": "Chat",
|
||||
"change_language": "Change Language"
|
||||
"change_language": "Language"
|
||||
},
|
||||
"home": {
|
||||
"logged_out_subtitle": "All the Book Buzz in Once Place",
|
||||
"logged_out_track_books": "Keep track of books you've read, want to read, and are currently reading.",
|
||||
"logged_out_share_friends": "Share your thoughts about what you're reading and see what your friends think of their books.",
|
||||
"logged_out_read_rate": "Rate, review, and recommmend books or something. I dunno. It's early days, my friends!",
|
||||
"logged_out_community_header": "A Look Inside the Hive",
|
||||
"logged_out_recent_reviews": "Recent Reviews",
|
||||
"logged_out_recent_updates": "Recent Updates",
|
||||
"logged_out_join_now": "Join Now!",
|
||||
"logged_in_subtitle": "Welcome!",
|
||||
"logged_in_updates": "Updates",
|
||||
"logged_in_interactions": "Interactions"
|
||||
"logged_out": {
|
||||
"subtitle": "All the Book Buzz in Once Place",
|
||||
"track_books": "Keep track of books you've read, want to read, and are currently reading.",
|
||||
"share_friends": "Share your thoughts about what you're reading and see what your friends think of their books.",
|
||||
"read_rate": "Rate, review, and recommmend books or something. I dunno. It's early days, my friends!",
|
||||
"community_header": "A Look Inside the Hive",
|
||||
"recent_reviews": "Recent Reviews",
|
||||
"recent_updates": "Recent Updates",
|
||||
"join_now": "Join Now!"
|
||||
},
|
||||
"logged_in": {
|
||||
"subtitle": "Welcome!",
|
||||
"updates": "Updates",
|
||||
"interactions": "Interactions"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"header": "Oops!",
|
||||
|
@ -46,15 +50,21 @@
|
|||
"header": "Search",
|
||||
"placeholder": "Search for Books",
|
||||
"button_text": "Search",
|
||||
"search_source_label": "Search Source",
|
||||
"search_source_help_button": "What's This?",
|
||||
"search_source_help_header": "What does \"Search Source\" mean?",
|
||||
"search_source_help_text": "This refers to where the search tries to look for data. Each source can be easily contributed to if you want to add missing books or correct errors.",
|
||||
"search_source_help_inventaire": "Sources and extends data from WikiData, the service structure that powers Wikipedia and the like. Offers as many language options as possible.",
|
||||
"search_source_help_openLibrary": "Sources data from Internet Archive and may provide a digital copy to read. Only offers English unless a work is in another language.",
|
||||
"search_by_label": "Search By",
|
||||
"search_by_title": "Title",
|
||||
"search_by_author": "Author",
|
||||
"search_source": {
|
||||
"label": "Search Source",
|
||||
"help": {
|
||||
"button": "What's This?",
|
||||
"header": "What does \"Search Source\" mean?",
|
||||
"text": "This refers to where the search tries to look for data. Each source can be easily contributed to if you want to add missing books or correct errors.",
|
||||
"inventaire": "Sources and extends data from WikiData, the service structure that powers Wikipedia and the like. Offers as many language options as possible.",
|
||||
"openLibrary": "Sources data from Internet Archive and may provide a digital copy to read. Only offers English unless a work is in another language."
|
||||
}
|
||||
},
|
||||
"search_by": {
|
||||
"label": "Search By",
|
||||
"title": "Title",
|
||||
"author": "Author"
|
||||
},
|
||||
"loading": "Loading...",
|
||||
"no_results": "None Found",
|
||||
"no_results_suggestion": "If you're expecting book data, go and help fill out the Inventaire database!",
|
||||
|
@ -73,28 +83,40 @@
|
|||
"reviews_written": "Total Reviews Written"
|
||||
},
|
||||
"api": {
|
||||
"account_already_logged_in": "You are already logged in! You cannot create an account or log in again.",
|
||||
"account_create_required_data_missing": "Could not create account because required data is missing.",
|
||||
"account_create_invalid_email": "The email address entered is not valid.",
|
||||
"account_create_invalid_username": "The username entered is not valid. Usernames must be at least 2 characters long and can only contain letters a–z, numbers 0–9, and underscores",
|
||||
"account_email_exists": "The email address entered is already in use.",
|
||||
"account_username_exists": "The username entered is already in use.",
|
||||
"account_email_send_fail": "Your account was created successfully, but we were unable to send the confirmation email!",
|
||||
"account_confirm_email": "A confirmation email has been sent to the address you specified. Please confirm your account using the link provided.",
|
||||
"account_create_success": "Account created successfully! You may now log in using the email address and password you provided.",
|
||||
"account_confirm_required_data_missing": "Could not confirm account because required data is missing.",
|
||||
"account_confirm_invalid_code": "The specified confirmation code is not valid.",
|
||||
"account_confirm_update_fail": "Something went wrong and we couldn't confirm your account. Please try again later!",
|
||||
"account_confirm_email_send_fail": "Your account has been confirmed, but we were unable to send the email notification about it. You can log in anyway.",
|
||||
"account_confirm_success_email": "Your account has been confirmed, and an email notification has been sent! You may now log in using your email address and password.",
|
||||
"account_confirm_success": "Your account has been confirmed! You may now log in using your email address and password.",
|
||||
"account_login_required_data_missing": "Could not attempt login because required data is missing.",
|
||||
"account_login_invalid_email": "The email address specified does not have an associated account.",
|
||||
"account_login_not_confirmed": "The specified account has not been confirmed. Please use the link you received to confirm your email address.",
|
||||
"account_login_invalid_password": "The password specified is not correct.",
|
||||
"account_login_success": "You have been successfully logged in! You will now be redirected to the home screen.",
|
||||
"account_validate_missing_token": "User not logged in: There is no login token to validate.",
|
||||
"account_validate_invalid_token": "User not logged in: The stored token is not a valid token.",
|
||||
"account_validate_renewed_token": "User logged in, and the token has been renewed."
|
||||
"not_logged_in": "You are not logged in.",
|
||||
"already_logged_in": "You are already logged in! You cannot create an account or log in again.",
|
||||
"account": {
|
||||
"username_exists": "The username entered is already in use.",
|
||||
"email_send_fail": "Your account was created successfully, but we were unable to send the confirmation email!",
|
||||
"email_exists": "The email address entered is already in use.",
|
||||
"create": {
|
||||
"fail": "Something went wrong and the account could not be created. Please try again later.",
|
||||
"required_data_missing": "Could not create account because required data is missing.",
|
||||
"invalid_email": "The email address entered is not valid.",
|
||||
"invalid_username": "The username entered is not valid. Usernames must be at least 2 characters long and can only contain letters a–z, numbers 0–9, and underscores",
|
||||
"success": "Account created successfully! You may now log in using the email address and password you provided."
|
||||
},
|
||||
"confirm": {
|
||||
"email": "A confirmation email has been sent to the address you specified. Please confirm your account using the link provided.",
|
||||
"required_data_missing": "Could not confirm account because required data is missing.",
|
||||
"invalid_code": "The specified confirmation code is not valid.",
|
||||
"update_fail": "Something went wrong and we couldn't confirm your account. Please try again later!",
|
||||
"email_send_fail": "Your account has been confirmed, but we were unable to send the email notification about it. You can log in anyway.",
|
||||
"success_email": "Your account has been confirmed, and an email notification has been sent! You may now log in using your email address and password.",
|
||||
"success": "Your account has been confirmed! You may now log in using your email address and password."
|
||||
},
|
||||
"login": {
|
||||
"required_data_missing": "Could not attempt login because required data is missing.",
|
||||
"invalid_email": "The email address specified does not have an associated account.",
|
||||
"not_confirmed": "The specified account has not been confirmed. Please use the link you received to confirm your email address.",
|
||||
"invalid_password": "The password specified is not correct.",
|
||||
"success": "You have been successfully logged in! You will now be redirected to the home screen."
|
||||
},
|
||||
"validate": {
|
||||
"missing_token": "User not logged in: There is no login token to validate.",
|
||||
"invalid_token": "User not logged in: The stored token is not a valid token.",
|
||||
"renewed_token": "User logged in, and the token has been renewed."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ async function routes(fastify, options) {
|
|||
if (request.isLoggedInUser) {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_already_logged_in',
|
||||
message: 'api.already_logged_in',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ async function routes(fastify, options) {
|
|||
const newUser = await account.createUser(formData.email, formData.username, formData.displayName, formData.password, fastify.canEmail);
|
||||
|
||||
if (typeof newUser.error !== 'undefined' && newUser.error !== false) {
|
||||
newUser.message = 'api.account_create_fail';
|
||||
newUser.message = 'api.account.create.fail';
|
||||
return reply.code(400).send(newUser);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ async function routes(fastify, options) {
|
|||
// If some of the default shelves are not created successfully, delete the user and send an error
|
||||
if (typeof defaultShelvesCreated.error !== 'undefined' && defaultShelvesCreated.error !== false) {
|
||||
account.deleteUser(newUser);
|
||||
defaultShelvesCreated.message = 'api.account_create_fail';
|
||||
defaultShelvesCreated.message = 'api.account.create.fail';
|
||||
return reply.code(400).send(defaultShelvesCreated);
|
||||
}
|
||||
|
||||
|
@ -72,14 +72,14 @@ async function routes(fastify, options) {
|
|||
console.error(email.err);
|
||||
return reply.send({
|
||||
error: true,
|
||||
message: 'api.account_email_send_fail',
|
||||
message: 'api.account.email_send_fail',
|
||||
newUser,
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
error: false,
|
||||
message: 'api.account_confirm_email',
|
||||
message: 'api.account.confirm.email',
|
||||
});
|
||||
});
|
||||
} catch (ex) {
|
||||
|
@ -89,7 +89,7 @@ async function routes(fastify, options) {
|
|||
|
||||
return reply.send({
|
||||
error: false,
|
||||
message: 'api.account_create_success',
|
||||
message: 'api.account.create.success',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -97,7 +97,7 @@ async function routes(fastify, options) {
|
|||
if (request.isLoggedInUser) {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_already_logged_in',
|
||||
message: 'api.already_logged_in',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -137,13 +137,13 @@ async function routes(fastify, options) {
|
|||
console.error(email.err);
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_confirm_email_send_fail',
|
||||
message: 'api.account.confirm.email_send_fail',
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
error: false,
|
||||
message: 'api.account_confirm_success_email',
|
||||
message: 'api.account.confirm.success_email',
|
||||
});
|
||||
})
|
||||
} catch (ex) {
|
||||
|
@ -152,7 +152,7 @@ async function routes(fastify, options) {
|
|||
|
||||
return reply.send({
|
||||
error: false,
|
||||
message: 'api.account_confirm_success',
|
||||
message: 'api.account.confirm.success',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -182,7 +182,7 @@ async function routes(fastify, options) {
|
|||
})
|
||||
.send({
|
||||
error: false,
|
||||
message: 'api.account_login_success',
|
||||
message: 'api.account.login.success',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -190,7 +190,7 @@ async function routes(fastify, options) {
|
|||
if (typeof request.cookies.token === "undefined") {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_validate_missing_token',
|
||||
message: 'api.account.validate.missing_token',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ async function routes(fastify, options) {
|
|||
if (!tokenIsValid) {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_validate_invalid_token',
|
||||
message: 'api.account.validate.invalid_token',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -214,7 +214,7 @@ async function routes(fastify, options) {
|
|||
})
|
||||
.send({
|
||||
error: false,
|
||||
message: 'api.account_validate_renewed_token',
|
||||
message: 'api.account.validate.renewed_token',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue