Translations for the profile page components

This commit is contained in:
garrettmills 2020-06-01 22:51:10 -05:00
parent 0bec18825e
commit 3e3e4e3ef3
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E
8 changed files with 173 additions and 49 deletions

View File

@ -41,7 +41,7 @@ const template = `
<th scope="row">{{ index + 1 }}</th> <th scope="row">{{ index + 1 }}</th>
<td v-for="col of definition.columns"> <td v-for="col of definition.columns">
<span v-if="typeof col.renderer === 'function'">{{ col.renderer(row[col.field]) }}</span> <span v-if="typeof col.renderer === 'function'">{{ col.renderer(row[col.field]) }}</span>
<span v-if="col.renderer === 'boolean'">{{ row[col.field] ? 'Yes' : 'No' }}</span> <span v-if="col.renderer === 'boolean'">{{ row[col.field] ? t['common.yes'] : t['common.no'] }}</span>
<span v-if="col.renderer !== 'boolean' && typeof col.renderer !== 'function'">{{ col.field in row ? row[col.field] : '-' }}</span> <span v-if="col.renderer !== 'boolean' && typeof col.renderer !== 'function'">{{ col.field in row ? row[col.field] : '-' }}</span>
</td> </td>
<td> <td>
@ -71,14 +71,26 @@ export default class ListingComponent extends Component {
access_msg = '' access_msg = ''
can_access = false can_access = false
t = {}
async vue_on_create() { async vue_on_create() {
this.t = await T(
'common.yes',
'common.no',
'common.not_permission',
'common.view',
'common.are_you_sure',
'common.action_resource_confirm',
'common.cancel',
'common.continue'
)
// Load the resource // Load the resource
this.resource_class = await resource_service.get(this.resource) this.resource_class = await resource_service.get(this.resource)
// Make sure we have permission // Make sure we have permission
if ( !(await this.resource_class.can('list')) ) { if ( !(await this.resource_class.can('list')) ) {
this.access_msg = 'Sorry, you do not have permission to view this resource.' this.access_msg = this.t['common.not_permission'].replace('ACTION', this.t['common.view'])
this.can_access = false this.can_access = false
return return
} else { } else {
@ -97,15 +109,15 @@ export default class ListingComponent extends Component {
async perform($event, action, row = undefined) { async perform($event, action, row = undefined) {
if ( action.confirm ) { if ( action.confirm ) {
message_service.modal({ message_service.modal({
title: 'Are you sure?', title: this.t['common.are_you_sure'],
message: `You are about to ${action.action}${row ? ' this '+this.resource_class.item : ''}. Do you want to continue?`, message: this.t['common.action_resource_confirm'].replace('ACTION', action.action).replace('RESOURCE', this.resource_class.item),
buttons: [ buttons: [
{ {
text: 'Cancel', text: this.t['common.cancel'],
type: 'close', type: 'close',
}, },
{ {
text: 'Continue', text: this.t['common.continue'],
class: ['btn', 'btn-primary'], class: ['btn', 'btn-primary'],
type: 'close', type: 'close',
on_click: async () => { on_click: async () => {

View File

@ -13,9 +13,9 @@ const template = `
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-8 offset-2 col-sm-4 offset-sm-0"> <div class="col-8 offset-2 col-sm-4 offset-sm-0">
<img src="/api/v1/profile/me/photo" alt="Profile Image" ref="photo" class="img-fluid"> <img src="/api/v1/profile/me/photo" :alt="t['profile.profile_photo']" ref="photo" class="img-fluid">
<div class="overlay"> <div class="overlay">
<button class="btn btn-outline-light" @click="on_profile_change_click">Change</button> <button class="btn btn-outline-light" @click="on_profile_change_click">{{ t['common.change'] }}</button>
</div> </div>
</div> </div>
<div class="col-sm-8 offset-sm-0 col-12 text-sm-left text-center pad-top"> <div class="col-sm-8 offset-sm-0 col-12 text-sm-left text-center pad-top">
@ -26,26 +26,26 @@ const template = `
</div> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item"> <li class="list-group-item">
<h4>Basic Profile</h4> <h4>{{ t['profile.basic_profile'] }}</h4>
<div class="row"> <div class="row">
<div class="col-12 col-md-6 form-group"> <div class="col-12 col-md-6 form-group">
<label for="coreid-profile-first-input">First Name</label> <label for="coreid-profile-first-input">{{ t['register.first_name'] }}</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="coreid-profile-first-input" id="coreid-profile-first-input"
placeholder="John" :placeholder="t['profile.placeholder_first']"
v-model="profile_first" v-model="profile_first"
@keyup="on_key_up($event)" @keyup="on_key_up($event)"
> >
</div> </div>
<div class="col-12 col-md-6 form-group"> <div class="col-12 col-md-6 form-group">
<label for="coreid-profile-last-input">Last Name</label> <label for="coreid-profile-last-input">{{ t['register.last_name'] }}</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="coreid-profile-last-input" id="coreid-profile-last-input"
placeholder="Doe" :placeholder="t['profile.placeholder_last']"
v-model="profile_last" v-model="profile_last"
@keyup="on_key_up($event)" @keyup="on_key_up($event)"
> >
@ -53,12 +53,12 @@ const template = `
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 form-group"> <div class="col-12 form-group">
<label for="coreid-profile-email-input">E-Mail Address</label> <label for="coreid-profile-email-input">{{ t['register.email'] }}</label>
<input <input
type="email" type="email"
class="form-control" class="form-control"
id="coreid-profile-email-input" id="coreid-profile-email-input"
placeholder="john.doe@contoso.com" :placeholder="t['profile.placeholder_email'] "
v-model="profile_email" v-model="profile_email"
ref="email_input" ref="email_input"
@keyup="on_key_up($event)" @keyup="on_key_up($event)"
@ -67,7 +67,7 @@ const template = `
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 form-group"> <div class="col-12 form-group">
<label for="coreid-profile-tag-input">Tagline</label> <label for="coreid-profile-tag-input">{{ t['profile.tagline'] }}</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -82,13 +82,13 @@ const template = `
{{ form_message }} {{ form_message }}
</li> </li>
<li class="list-group-item" v-if="!user_id || user_id === 'me'"> <li class="list-group-item" v-if="!user_id || user_id === 'me'">
<h4>Password</h4> <h4>{{ t['password.password'] }}</h4>
<p class="font-italic" v-if="last_reset">Your password was last changed on {{ last_reset }}.</p> <p class="font-italic" v-if="last_reset">{{ t['profile.pw_last_reset'].replace('LAST_RESET', last_reset) }}</p>
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
@click="change_password" @click="change_password"
>Change Password</button> >{{ t['password.change'] }}</button>
</li> </li>
<li class="list-group-item" v-if="!has_mfa && (!user_id || user_id === 'me')"> <li class="list-group-item" v-if="!has_mfa && (!user_id || user_id === 'me')">
<h4>Multi-factor Authentication</h4> <h4>Multi-factor Authentication</h4>
@ -97,49 +97,49 @@ const template = `
<button class="btn btn-success btn-sm" type="button" @click="enable_mfa">Enable MFA</button> <button class="btn btn-success btn-sm" type="button" @click="enable_mfa">Enable MFA</button>
</li> </li>
<li class="list-group-item" v-if="has_mfa && (!user_id || user_id === 'me')"> <li class="list-group-item" v-if="has_mfa && (!user_id || user_id === 'me')">
<h4>Multi-factor Authentication</h4> <h4>{{ t['mfa.mfa'] }}</h4>
<p class="font-italic">MFA was enabled for your account on {{ mfa_enable_date }}.</p> <p class="font-italic">{{ t['profile.mfa_enabled_on'].replace('MFA_ENABLED', mfa_enable_date) }}</p>
<button <button
class="btn btn-danger btn-sm" class="btn btn-danger btn-sm"
type="button" type="button"
@click="disable_mfa" @click="disable_mfa"
>Disable MFA</button> >{{ t['mfa.disable'] }}</button>
<h6 class="pad-top">App Passwords</h6> <h6 class="pad-top">{{ t['profile.app_pws'] }}</h6>
<p>App passwords are specially generated passwords that allow you to sign into legacy services with your {{ app_name }} account.</p> <p>{{ t['profile.app_pw_1'].replace('APP_NAME', app_name) }}</p>
<p>You should only use this to authenticate against a service that needs to repeatedly use your password on your behalf (e.g. e-mail clients).</p> <p>{{ t['profile.app_pw_2'] }}</p>
<p>Use these with caution, as they can bypass your multi-factor authentication.</p> <p>{{ t['profile.app_pw_3'] }}</p>
<p class="font-italic text-muted" v-if="app_passwords.length > 0">You have {{ app_passwords.length }} app {{ app_passwords.length === 1 ? 'password' : 'passwords' }} associated with your account.</p> <p class="font-italic text-muted" v-if="app_passwords.length > 0">{{ t['profile.app_pw_remaining.'+(app_passwords.length === 1 ? 'one' : 'many')].replace('NUM_PWS', app_passwords.length) }}</p>
<ul class="list-group mb-4" v-if="app_passwords.length > 0"> <ul class="list-group mb-4" v-if="app_passwords.length > 0">
<li class="list-group-item" v-for="pw of app_passwords"> <li class="list-group-item" v-for="pw of app_passwords">
<div class="row"> <div class="row">
<div class="col-9"> <div class="col-9">
{{ pw.name }} {{ pw.name }}
<br><span class="text-muted font-italic">Issued: {{ pw.created }}</span> <br><span class="text-muted font-italic">{{ t['profile.issued'] }} {{ pw.created }}</span>
</div> </div>
<div class="col-3 my-auto"> <div class="col-3 my-auto">
<button <button
class="btn btn-sm btn-danger" class="btn btn-sm btn-danger"
type="button" type="button"
@click="deactivate_app_password($event, pw)" @click="deactivate_app_password($event, pw)"
>Deactivate</button> >{{ t['common.deactivate'] }}</button>
</div> </div>
</div> </div>
</li> </li>
</ul> </ul>
<button class="btn btn-sm btn-primary" @click="on_click_generate_app_password">Generate New</button> <button class="btn btn-sm btn-primary" @click="on_click_generate_app_password">{{ t['profile.gen_new'] }}</button>
<h6 class="pad-top">Recovery Codes</h6> <h6 class="pad-top">{{ t['profile.recovery_codes'] }}</h6>
<p>Recovery codes can be used to regain access to your account in the event that you lose access to the device that generates your MFA codes.</p> <p>{{ t['profile.recovery_1'] }}</p>
<span v-if="!has_mfa_recovery"> <span v-if="!has_mfa_recovery">
<p class="font-italic">No recovery codes have been generated for your account.</p> <p class="font-italic">{{ t['profile.no_recovery'] }}</p>
<button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">Generate Recovery Codes</button> <button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">{{ t['profile.generate_recovery'] }}</button>
</span> </span>
<span v-if="has_mfa_recovery"> <span v-if="has_mfa_recovery">
<p class="font-italic">Recovery codes were generate for your account on {{ mfa_recovery_date }}. <span v-if="mfa_recovery_codes === 1">There is only 1 recovery code remaining.</span><span v-if="mfa_recovery_codes !== 1">There are {{ mfa_recovery_codes }} recovery codes remaining.</span></p> <p class="font-italic">{{ t['profile.recovery_gen_on'].replace('MFA_RECOVERY', mfa_recovery_date) }} {{ t['profile.codes_remaining.'+(mfa_recovery_codes === 1 ? 'one' : 'many')].replace('NUM_CODES', mfa_recovery_codes) }}</p>
<button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">Re-generate Recovery Codes</button> <button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">{{ t['profile.regenerate_recovery'] }}</button>
</span> </span>
</li> </li>
</ul> </ul>
@ -179,10 +179,48 @@ export default class EditProfileComponent extends Component {
ready = false ready = false
app_passwords = [] app_passwords = []
app_name = ''
t = {}
on_key_up = ($event) => {} on_key_up = ($event) => {}
vue_on_create() { async vue_on_create() {
this.t = await T(
'profile.profile_photo',
'common.change',
'profile.basic_profile',
'register.first_name',
'profile.placeholder_first',
'register.last_name',
'profile.placeholder_last',
'register.email',
'profile.placeholder_email',
'profile.tagline',
'password.password',
'password.change',
'profile.pw_last_reset',
'mfa.mfa',
'profile.mfa_enabled_on',
'mfa.disable',
'profile.app_pws',
'profile.issued',
'common.deactivate',
'profile.app_pw_remaining.one',
'profile.app_pw_remaining.many',
'profile.gen_new',
'profile.recovery_codes',
'profile.recovery_1',
'profile.no_recovery',
'profile.generate_recovery',
'profile.recovery_gen_on',
'profile.codes_remaining.one',
'profile.codes_remaining.many',
'profile.regenerate_recovery',
'profile.app_pw_1',
'profile.app_pw_2',
'profile.app_pw_3'
)
this.app_name = session.get('app.name') this.app_name = session.get('app.name')
this.load().then(() => { this.load().then(() => {
this.ready = true this.ready = true

View File

@ -13,27 +13,27 @@ const template = `
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Generate App-Password</h5> <h5 class="modal-title">{{ t['profile.generate_app_pw'] }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" :aria-label="t['common.close']">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group" v-if="!display_password"> <div class="form-group" v-if="!display_password">
<label :for="uuid">App Name</label> <label :for="uuid">{{ t['profile.app_name'] }}</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
:id="uuid" :id="uuid"
v-model="name" v-model="name"
@keyup="on_name_change" @keyup="on_name_change"
placeholder="My really cool e-mail client" :placeholder="t['profile.cool_email_client']"
:disabled="!enable_form" :disabled="!enable_form"
ref="input" ref="input"
> >
</div> </div>
<div v-if="display_password"> <div v-if="display_password">
The app password for <code>{{ name }}</code> was generated successfully. Copy the password below and use it in <code>{{ name }}</code> to sign in. Note that, once you close this window, you will no longer be able to view this password. {{ t['profile.app_pw_success'].replace(/APP_NAME/g, name) }}
</div> </div>
<div v-if="display_password" class="text-center pad-top"> <div v-if="display_password" class="text-center pad-top">
<pre><code>{{ display_password }}</code></pre> <pre><code>{{ display_password }}</code></pre>
@ -46,21 +46,21 @@ const template = `
data-dismiss="modal" data-dismiss="modal"
@click="$emit('modal-cancel')" @click="$emit('modal-cancel')"
v-if="!display_password" v-if="!display_password"
>Cancel</button> >{{ t['common.cancel'] }}</button>
<button <button
type="button" type="button"
class="btn btn-primary" class="btn btn-primary"
:disabled="!valid || !enable_form" :disabled="!valid || !enable_form"
@click="generate_pw" @click="generate_pw"
v-if="!display_password" v-if="!display_password"
>Generate</button> >{{ t['common.generate'] }}</button>
<button <button
type="button" type="button"
class="btn btn-primary" class="btn btn-primary"
v-if="display_password" v-if="display_password"
data-dismiss="modal" data-dismiss="modal"
@click="$emit('modal-success')" @click="$emit('modal-success')"
>Close</button> >{{ t['common.close'] }}</button>
</div> </div>
</div> </div>
</div> </div>
@ -77,8 +77,19 @@ export default class AppPasswordFormComponent extends Component {
uuid = '' uuid = ''
enable_form = true enable_form = true
display_password = '' display_password = ''
t = {}
async vue_on_create() {
this.t = await T(
'profile.generate_app_pw',
'profile.app_name',
'profile.cool_email_client',
'profile.app_pw_success',
'common.cancel',
'common.generate',
'common.close'
)
vue_on_create() {
this.uuid = utility.uuid() this.uuid = utility.uuid()
} }

View File

@ -5,8 +5,8 @@ const template = `
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content" v-if="ready"> <div class="modal-content" v-if="ready">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Change Profile Photo</h5> <h5 class="modal-title">{{ t['profile.change_photo'] }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" :aria-label="t['common.close']">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
@ -17,7 +17,7 @@ const template = `
<button <button
class="btn btn-primary" class="btn btn-primary"
@click="do_upload" @click="do_upload"
>Change</button> >{{ t['common.change'] }}</button>
</div> </div>
</div> </div>
</div> </div>
@ -30,6 +30,15 @@ export default class ProfilePhotoUploaderComponent extends Component {
static get params() { return [] } static get params() { return [] }
ready = false ready = false
t = {}
async vue_on_create() {
this.t = await T(
'profile.change_photo',
'common.change',
'common.close'
)
}
show() { show() {
this.ready = true this.ready = true

View File

@ -8,6 +8,8 @@ module.exports = exports = {
invalid: 'Invalid', invalid: 'Invalid',
unnamed: '(unnamed)', unnamed: '(unnamed)',
yes: 'Yes',
no: 'No',
deny: 'Deny', deny: 'Deny',
grant: 'Grant Access', grant: 'Grant Access',
back: 'Back', back: 'Back',
@ -15,6 +17,11 @@ module.exports = exports = {
cancel: 'Cancel', cancel: 'Cancel',
request: 'Request', request: 'Request',
continue: 'Continue', continue: 'Continue',
generate: 'Generate',
close: 'Close',
change: 'Change',
deactivate: 'Deactivate',
view: 'View',
unknown_error: 'An unknown error has occurred, and we are unable to continue at this time.', unknown_error: 'An unknown error has occurred, and we are unable to continue at this time.',
invalid_resolver: 'Invalid locale resolver.', invalid_resolver: 'Invalid locale resolver.',
@ -26,4 +33,7 @@ module.exports = exports = {
invalid_json: 'must be valid JSON.', invalid_json: 'must be valid JSON.',
not_permission: 'Sorry, you do not have permission to ACTION this resource.', not_permission: 'Sorry, you do not have permission to ACTION this resource.',
item_saved: 'The ITEM was saved.', item_saved: 'The ITEM was saved.',
are_you_sure: 'Are you sure?',
action_resource_confirm: 'You are about to ACTION this RESOURCE. Do you want to continue?',
} }

View File

@ -1,4 +1,5 @@
module.exports = exports = { module.exports = exports = {
mfa: 'Multi-factor Authentication',
challenge_prompt: 'Your account has multi-factor authentication enabled. Please enter the code generated by your authenticator app to continue.', challenge_prompt: 'Your account has multi-factor authentication enabled. Please enter the code generated by your authenticator app to continue.',
mfa_code: { one: 'MFA Code', many: 'MFA Codes' }, mfa_code: { one: 'MFA Code', many: 'MFA Codes' },
lost_device: 'Lost your MFA device?', lost_device: 'Lost your MFA device?',

View File

@ -7,5 +7,6 @@ module.exports = exports = {
new_password: 'New Password', new_password: 'New Password',
confirm_password: 'Confirm the Password', confirm_password: 'Confirm the Password',
change: 'Change Password', change: 'Change Password',
password: 'Password',
reset_success: 'Your password was reset. For security reasons, you will be asked to sign-in again.', reset_success: 'Your password was reset. For security reasons, you will be asked to sign-in again.',
} }

View File

@ -0,0 +1,42 @@
module.exports = exports = {
generate_app_pw: 'Generate App-Password',
app_pws: 'App Passwords',
app_name: 'App Name',
cool_email_client: 'My really cool e-mail client',
app_pw_success: 'The app password for <code>APP_NAME</code> was generated successfully. Copy the password below and use it in <code>APP_NAME</code> to sign in. Note that, once you close this window, you will no longer be able to view this password.',
change_photo: 'Change Profile Photo',
profile_photo: 'Profile Photo',
placeholder_first: 'John',
placeholder_last: 'Doe',
placeholder_email: 'john.doe@contoso.com',
tagline: 'Tagline',
basic_profile: 'Basic Profile',
pw_last_reset: 'Your password was last changed on LAST_RESET.',
mfa_enabled_on: 'MFA was enabled for your account on MFA_ENABLED.',
app_pw_1: 'App passwords are specially generated passwords that allow you to sign into legacy services with your APP_NAME account.',
app_pw_2: 'You should only use this to authenticate against a service that needs to repeatedly use your password on your behalf (e.g. e-mail clients).',
app_pw_3: 'Use these with caution, as they can bypass your multi-factor authentication.',
app_pw_remaining: {
one: 'You have NUM_PWS app password associated with your account.',
many: 'You have NUM_PWS app passwords associated with your account.',
},
issued: 'Issued:',
gen_new: 'Generate New',
recovery_codes: 'Recovery Codes',
recovery_1: 'Recovery codes can be used to regain access to your account in the event that you lose access to the device that generates your MFA codes.',
no_recovery: 'No recovery codes have been generated for your account.',
generate_recovery: 'Generate Recovery Codes',
recovery_gen_on: 'Recovery codes were generate for your account on MFA_RECOVERY.',
codes_remaining: {
one: 'There is only 1 recovery code remaining.',
many: 'There are NUM_CODES recovery codes remaining.',
},
regenerate_recovery: 'Re-generate Recovery Codes',
}