that will generate locale specific out for Action Triggers. If you send notices in
multiple languages, we recommend putting some words to that effect in your notice
templates. The template, message and message title can all be localized. To use the
feature the following new UI elements have been added:
- When you double-click on an Event Definition under Notifications / Action Triggers
to edit it there will be a tab option for Edit Alternate Template if the reactor is
ProcessTemplate, SendEmail or SendSMS.
- In the Patron Registration and Patron Editor screens staff members may now select a
locale for a patron and edit it in the Patron Preferred Language field.
- Patrons may set their own locale in the My Account interface off the OPAC by going to
Preferences -> Personal Information and setting the Preferred Language field.
The templates used on the Edit Definition tab are the defaults that are used if there are
no alternate templates available that match the preferred language. If alternate templates
are available the system will use a locale that is an exact match and then if failing that
use one where the language code matches and then fall back to the default one.
For example, if a patron has a locale of fr-CA and there are templates for both fr-CA and
fr-FR it will use the fr-CA. If the fr-CA template was deleted it would fall back on using
the fr-FR for the patron since it at least shares the same base language.
Valid locales are the codes defined in the i18n_locale table in the config schema.
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Jane Sandberg <sandbergja@gmail.com>
<field reporter:label="Is Error" name="is_error" reporter:datatype="bool"/>
<field reporter:label="Events" name="events" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Error Events" name="error_events" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Output Locale" name="locale" reporter:datatype="text"/>
</fields>
<links>
<link field="events" reltype="has_many" key="template_output" map="" class="atev"/>
</permacrud>
</class>
+ <class id="atevalt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::alternate_template" oils_persist:tablename="action_trigger.alternate_template" reporter:label="Alternate Action Trigger Templates">
+ <fields oils_persist:primary="id" oils_persist:sequence="action_trigger.alternate_template_id_seq">
+ <field reporter:label="Alternate Template ID" name="id" reporter:datatype="id"/>
+ <field reporter:label="Enabled" name="active" reporter:datatype="bool"/>
+ <field reporter:label="Template" name="template" reporter:datatype="text"/>
+ <field reporter:label="Template Locale" name="locale" reporter:datatype="link" oils_obj:required="true"/>
+ <field reporter:label="Message Title" name="message_title" reporter:datatype="text"/>
+ <field reporter:label="Message Template" name="message_template" reporter:datatype="text"/>
+ <field reporter:label="Event Definition" name="event_def" reporter:datatype="link"/>
+ </fields>
+ <links>
+ <link field="event_def" reltype="has_a" key="id" map="" class="atevdef"/>
+ <link field="locale" relteype="has_a" key="code" map="" class="i18n_l"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <create permission="ADMIN_TRIGGER_EVENT_DEF CREATE_TRIGGER_EVENT_DEF">
+ <context link="event_def" field="owner"/>
+ </create>
+ <retrieve permission="ADMIN_TRIGGER_EVENT_DEF VIEW_TRIGGER_EVENT_DEF">
+ <context link="event_def" field="owner"/>
+ </retrieve>
+ <update permission="ADMIN_TRIGGER_EVENT_DEF UPDATE_TRIGGER_EVENT_DEF">
+ <context link="event_def" field="owner"/>
+ </update>
+ <delete permission="ADMIN_TRIGGER_EVENT_DEF DELETE_TRIGGER_EVENT_DEF">
+ <context link="event_def" field="owner"/>
+ </delete>
+ </actions>
+ </permacrud>
+ </class>
+
+
<class id="atevdefg" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::event_def_group" oils_persist:tablename="action_trigger.event_def_group" reporter:label="Trigger Event Definition Group" oils_persist:restrict_primary="100">
<fields oils_persist:primary="id" oils_persist:sequence="action_trigger.event_def_group_id_seq">
<field reporter:label="Group ID" name="id" reporter:datatype="id"/>
<field reporter:label="Reservations" name="reservations" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="User Activity Entries" name="usr_activity" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="User/Working Location Map" name="usr_work_ou_map" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Patron Preferred Language" name="locale" reporter:datatype="link"/>
</fields>
<links>
<link field="demographic" reltype="might_have" key="id" map="" class="rud"/>
<link field="reservations" reltype="has_many" key="usr" map="" class="bresv"/>
<link field="usr_activity" reltype="has_many" key="usr" map="" class="auact"/>
<link field="usr_work_ou_map" reltype="has_many" key="usr" map="" class="puwoum"/>
+ <link field="locale" reltype="has_a" key="code" map="" class="i18n_l"/>
</links>
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
<actions>
<div class="text-right">
<button class="btn btn-outline-dark mr-3" (click)="back()">← Back to Notifications/Action Triggers</button>
</div>
- </ng-template>
+ </ng-template>
+ </li>
+ <li ngbNavItem="'alt'" *ngIf=this.evtAltEligible>
+ <a ngbNavLink i18n>Edit Alternate Template</a>
+ <ng-template ngbNavContent>
+ <ng-template #textAreaTemplate let-field="field" let-record="record">
+ <textarea class="form-control" name="{{field.name}}"
+ [readonly]="field.readOnly" [required]="field.isRequired()"
+ [ngModel]="record[field.name]()"
+ (ngModelChange)="record[field.name]($event)" style="height: 600px;">
+ </textarea>
+ </ng-template>
+ <h3 class="mb-3">Alternate Templates</h3>
+ <eg-grid #altTemplateGrid idlClass="atevalt" [dataSource]="altTemplateDataSource"
+ showFields="active,locale" persistKey="admin.local.triggers.atevalt"
+ (onRowActivate)="editSelected([$event])">
+ <eg-grid-toolbar-button label="New Template" i18n-label
+ [action]="createNewAltTemplate"></eg-grid-toolbar-button>
+ <eg-grid-toolbar-action label="Edit Template" i18n-label
+ [action]="editSelected"></eg-grid-toolbar-action>
+ <eg-grid-toolbar-action label="Delete Selected" i18n-label
+ (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+ </eg-grid>
+ <eg-fm-record-editor #altTemplateDialog idlClass="atevalt"
+ [fieldOptions]="{template:{customTemplate:{template:textAreaTemplate}},message_template:{customTemplate:{template:textAreaTemplate}}}"
+ fieldOrder="active,locale,template,message_title,message_template"
+ [preloadLinkedValues]="true"
+ hiddenFields="event_def,id"></eg-fm-record-editor>
+ </ng-template>
</li>
<li ngbNavItem="'env'">
<a ngbNavLink i18n>Edit Environment</a>
evtDefId: number;
evtDefName: String;
+ evtReactor: string;
+ evtAltEligible: Boolean = false;
testErr1: String = '';
testErr2: String = '';
testResult: String = '';
testDone: Boolean = false;
+ altTemplateDataSource: GridDataSource = new GridDataSource();
envDataSource: GridDataSource = new GridDataSource();
paramDataSource: GridDataSource = new GridDataSource();
- editTab: 'def' | 'env' | 'param' | 'test' = 'def';
+ editTab: 'def' | 'alt' | 'env' | 'param' | 'test' = 'def';
@ViewChild('paramDialog') paramDialog: FmRecordEditorComponent;
@ViewChild('envDialog') envDialog: FmRecordEditorComponent;
+ @ViewChild('altTemplateDialog') altTemplateDialog: FmRecordEditorComponent;
@ViewChild('envGrid') envGrid: GridComponent;
@ViewChild('paramGrid') paramGrid: GridComponent;
+ @ViewChild('altTemplateGrid') altTemplateGrid: GridComponent;
@ViewChild('updateSuccessString') updateSuccessString: StringComponent;
@ViewChild('updateFailedString') updateFailedString: StringComponent;
this.evtDefName = rec.name();
});
+ // get current event def reactor to decide if the alt template tab should show
+ this.pcrud.search('atevdef',
+ {id: this.evtDefId}, {}).toPromise().then(rec => {
+ this.evtReactor = rec.reactor();
+ if ('ProcessTemplate SendEmail SendSMS'.indexOf(this.evtReactor) > -1)
+ { this.evtAltEligible = true; }
+ });
+
this.envDataSource.getRows = (pager: Pager, sort: any[]) => {
return this.pcrud.search('atenv',
{event_def: this.evtDefId}, {});
};
+ this.altTemplateDataSource.getRows = (pager: Pager, sort: any[]) => {
+ return this.pcrud.search('atevalt',
+ {event_def: this.evtDefId}, {});
+ };
+
this.paramDataSource.getRows = (pager: Pager, sort: any[]) => {
return this.pcrud.search('atevparam',
{event_def: this.evtDefId}, {});
this.createNewThing(this.envDialog, this.envGrid, 'atenv');
}
+ createNewAltTemplate = () => {
+ this.createNewThing(this.altTemplateDialog, this.altTemplateGrid, 'atevalt');
+ }
+
createNewParam = () => {
this.createNewThing(this.paramDialog, this.paramGrid, 'atevparam');
}
let currentGrid;
if (idlThings[0].classname === 'atenv') {
currentGrid = this.envGrid;
+ } else if (idlThings[0].classname === 'atevalt') {
+ currentGrid = this.altTemplateGrid;
} else {
currentGrid = this.paramGrid;
}
if (selectedRecord.classname === 'atenv') {
currentDialog = this.envDialog;
currentGrid = this.envGrid;
+ } else if (selectedRecord.classname === 'atevalt') {
+ currentDialog = this.altTemplateDialog;
+ currentGrid = this.altTemplateGrid;
} else {
currentDialog = this.paramDialog;
currentGrid = this.paramGrid;
}
);
+__PACKAGE__->register_method(
+ method => "update_passwd",
+ api_name => "open-ils.actor.user.locale.update",
+ signature => {
+ desc => "Update the operator's i18n locale",
+ params => [
+ { desc => 'Authentication token', type => 'string' },
+ { desc => 'New locale', type => 'string' },
+ { desc => 'Current password', type => 'string' }
+ ],
+ return => {desc => '1 on success, Event on error or incorrect current password'}
+ }
+);
+
sub update_passwd {
my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
my $e = new_editor(xact=>1, authtoken=>$auth);
} elsif( $api =~ /email/o ) {
$db_user->email($new_val);
$at_event++;
+
+ } elsif( $api =~ /locale/o ) {
+ $db_user->locale($new_val);
+ $at_event++;
}
}
$self->environment->{usr_message}{title} = $self->event->event_def->message_title;
$self->environment->{user_data} = $self->user_data;
+ my ($usr_locale, $alt_templates, $query, $query_result, $new_template_id);
+ my $reactor = $self->environment->{event}->event_def->reactor;
+ $query = {
+ select => { atevalt => ['id', 'locale'] },
+ from => 'atevalt',
+ where => {
+ event_def => $self->environment->{event}->event_def->id,
+ active => 't'
+ }
+ };
+ my $e = new_editor(xact=>1);
+ if ($reactor) {
+ if ( $reactor eq 'SendEmail'
+ or $reactor eq 'ProcessTemplate'
+ or $reactor eq 'SendSMS') {
+ $query_result = $e->json_query($query);
+ $alt_templates = $query_result;
+ $query = {
+ select => { au => ['locale'] },
+ from => 'au',
+ where => { id => $self->environment->{event}->target }
+ };
+ $query_result = $e->json_query($query);
+ $usr_locale = @$query_result[0]->{locale};
+ if ($alt_templates and @$alt_templates and $usr_locale) {
+ foreach (@$alt_templates) {
+ if ($_->{locale} eq $usr_locale) {
+ $new_template_id = $_->{id};
+ $self->environment->{tt_locale} = $_->{locale};
+ last;
+ } else { #attempt a lanuage if not locale match
+ if ((split /\p{Dash}/,$_->{locale})[0] eq (split /\p{Dash}/,$usr_locale)[0]) {
+ $new_template_id = $_->{id};
+ $self->environment->{tt_locale} = $_->{locale};
+ }
+ }
+ }
+ }
+ if ($new_template_id) {
+ $query = {
+ select => { atevalt => ['template','message_template','message_title'] },
+ from => 'atevalt',
+ where => { id => $new_template_id }
+ };
+ $query_result = $e->json_query($query);
+ $self->environment->{template} = @$query_result[0]->{template};
+ $self->environment->{usr_message}{template} = @$query_result[0]->{message_template};
+ $self->environment->{usr_message}{title} = @$query_result[0]->{message_title};
+ }
+ }
+ }
$current_environment = $self->environment;
$self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
my $t_o = Fieldmapper::action_trigger::event_output->new;
$t_o->data( ($error) ? $error : $output );
$t_o->is_error( ($error) ? 't' : 'f' );
+ $t_o->locale($env->{tt_locale});
$logger->info("trigger: writing " . length($t_o->data) . " bytes to template output");
$env->{EventProcessor}->editor->xact_begin;
return $self->load_myopac_update_email if $path =~ m|opac/myopac/update_email|;
return $self->load_myopac_update_password if $path =~ m|opac/myopac/update_password|;
return $self->load_myopac_update_username if $path =~ m|opac/myopac/update_username|;
+ return $self->load_myopac_update_locale if $path =~ m|opac/myopac/update_locale|;
return $self->load_myopac_bookbags if $path =~ m|opac/myopac/lists|;
return $self->load_myopac_bookbag_print if $path =~ m|opac/myopac/list/print|;
return $self->load_myopac_bookbag_update if $path =~ m|opac/myopac/list/update|;
{
flesh => 2,
flesh_fields => {
- au => [qw/card home_ou addresses ident_type billing_address waiver_entries/, @extra_flesh],
+ au => [qw/card home_ou addresses ident_type locale billing_address waiver_entries/, @extra_flesh],
"aou" => ["billing_address"]
}
}
return $self->generic_redirect($url);
}
+sub load_myopac_update_locale {
+ my $self = shift;
+ my $e = $self->editor;
+ my $ctx = $self->ctx;
+ my $lang = $self->cgi->param('pref_lang') || '';
+ my $current_pw = $self->cgi->param('current_pw') || '';
+
+ $self->prepare_extended_user_info;
+
+ my $locs = $U->simplereq(
+ 'open-ils.cstore',
+ "open-ils.cstore.direct.config.i18n_locale.search.atomic",
+ { "code" => { "!=" => undef } }
+ );
+
+ my %user_locales;
+ foreach my $l (@$locs) { $user_locales{$l->code} = $l->name; }
+ $self->ctx->{i18n_locales} = \%user_locales;
+
+ return Apache2::Const::OK
+ unless $self->cgi->request_method eq 'POST';
+
+ if($lang ne $e->requestor->locale) {
+ my $evt = $U->simplereq(
+ 'open-ils.actor',
+ 'open-ils.actor.user.locale.update',
+ $e->authtoken, $lang, $current_pw);
+
+ if($U->event_equals($evt, 'INCORRECT_PASSWORD')) {
+ $ctx->{password_incorrect} = 1;
+ return Apache2::Const::OK;
+ }
+ }
+
+ my $url = $self->apache->unparsed_uri;
+ $url =~ s/update_locale/prefs/;
+
+ return $self->generic_redirect($url);
+}
+
sub load_myopac_update_password {
my $self = shift;
my $e = $self->editor;
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
expire_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT (now() + '3 years'::INTERVAL),
claims_never_checked_out_count INT NOT NULL DEFAULT 0,
- last_update_time TIMESTAMP WITH TIME ZONE
+ last_update_time TIMESTAMP WITH TIME ZONE,
+ locale TEXT REFERENCES config.i18n_locale(code) INITIALLY DEFERRED
);
COMMENT ON TABLE actor.usr IS $$
User objects
CONSTRAINT ev_def_name_owner_once UNIQUE (owner, name)
);
+CREATE TABLE action_trigger.alternate_template (
+ id SERIAL,
+ event_def INTEGER REFERENCES action_trigger.event_definition(id) INITIALLY DEFERRED,
+ template TEXT,
+ active BOOLEAN DEFAULT TRUE,
+ locale TEXT REFERENCES config.i18n_locale(code) INITIALLY DEFERRED,
+ message_title TEXT,
+ message_template TEXT,
+ UNIQUE (event_def,locale)
+);
+
CREATE OR REPLACE FUNCTION action_trigger.check_valid_retention_interval()
RETURNS TRIGGER AS $_$
BEGIN
id BIGSERIAL PRIMARY KEY,
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_error BOOLEAN NOT NULL DEFAULT FALSE,
- data TEXT NOT NULL
+ data TEXT NOT NULL,
+ locale TEXT
);
CREATE TABLE action_trigger.event (
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('xxxx', :eg_version);
+
+CREATE TABLE action_trigger.alternate_template (
+ id SERIAL,
+ event_def INTEGER REFERENCES action_trigger.event_definition(id) INITIALLY DEFERRED,
+ template TEXT,
+ active BOOLEAN DEFAULT TRUE,
+ message_title TEXT,
+ message_template TEXT,
+ locale TEXT REFERENCES config.i18n_locale(code) INITIALLY DEFERRED,
+ UNIQUE (event_def,locale)
+);
+
+ALTER TABLE actor.usr ADD COLUMN locale TEXT REFERENCES config.i18n_locale(code) INITIALLY DEFERRED;
+
+ALTER TABLE action_trigger.event_output ADD COLUMN locale TEXT;
+
+COMMIT;
max-width: 100px !important;
}
+.mod-control{
+ max-width: 150px !important;
+}
+
.card-body:empty{
display:none;
}
</tr>
<tr>
+ <td class='color_4 light_border'>[% l("Preferred Language") %]</td>
+ <td class='light_border'>[% ctx.user.locale.name | html %]</td>
+ <td>
+ <span class='light_border'><a class="btn btn-sm btn-action" href='update_locale'
+ title="[% l('Update Preferred Language') %]"><i class="fas fa-user-cog"></i> [% l('Change') %]</a></span>
+ </td>
+ </tr>
+
+ <tr>
<td class='color_4 light_border'>[% l("Home Library") %]</td>
<td class='light_border'>
[% ctx.get_aou(ctx.user.home_ou.parent_ou).name %]<br/>
--- /dev/null
+[% PROCESS "opac/parts/header.tt2";
+ PROCESS "opac/parts/misc_util.tt2";
+ WRAPPER "opac/parts/myopac/base.tt2";
+ myopac_page = "prefs" %]
+<h3 class="sr-only">[% l('Update Preferred Language') %]</h3>
+<div id='myopac_summary_div' style="padding:0px;">
+
+ <div class="header_middle">
+ <span class="float-left">[% l('Update Preferred Language') %]</span>
+ </div>
+
+[% IF ctx.password_incorrect %]
+ <div id='account-update-email-error'>
+ [% |l %]Your current password was not correct.[% END %]
+ </div>
+[% END %]
+
+<form method='post' id='account-update-email' autocomplete='off'>
+ [% IF CGI.param("return_to_referer") %]
+ <input type="hidden" name="redirect_to" value="[% ctx.referer | html %]" />
+ [% END %]
+ <table>
+ <tr><td>[% l('Current Preferred Language') %]</td><td>[% ctx.user.locale.name | html %]</td></tr>
+ <tr><td>[% l('Current Password') %]</td><td><input type='password' name='current_pw'/></td></tr>
+ <tr><td>[% l('New Preferred Language') %]</td>
+ <td class="px-3">
+ <select class="d-inline-block form-control mod-control" name="pref_lang" id="pref_lang">
+ [% FOREACH i18n IN ctx.i18n_locales %]
+ <option value='[% i18n.key | html %]'>[% l(i18n.value) %]
+ [% END %]
+ </select>
+ </td>
+ </tr>
+ </table>
+ <button class="btn btn-confirm m-2" type='submit'><i class="fas fa-save"></i>Save Changes</button>
+</form>
+
+[% END %]
</div>
</div>
+<!-- LOCALE -->
+<div class="row reg-field-row" ng-show="show_field('au.locale')">
+ [% draw_field_label('au', 'locale') %]
+ <div class="col-md-3 reg-field-input">
+ <select
+ class="form-control"
+ aria-labelledby="{{idl_fields.au.locale.name}}"
+ ng-model="patron.locale"
+ ng-blur="handle_field_changed(patron, 'locale')"
+ ng-options="loc.name() for loc in locales track by loc.code()">
+ </select>
+ </div>
+</div>
+
<!-- EMAIL -->
<div class="row reg-field-row" ng-show="show_field('au.email')">
[% draw_field_label('au', 'email') %]
<div class="col-md-7">{{patron().ident_value2()}}</div>
</div>
<div class="row">
+ <div class="col-md-5">[% l('Pref Language') %] <span ng-if="hasLocaleName" class="locale"></span></div>
+ <div class="col-md-7">{{patron().locale().name()}}</div>
+ </div>
+ <div class="row">
<div class="col-md-5">[% l('Legal Name') %]</div>
<div class="col-md-7">
[% l('[_1] [_2], [_3] [_4] [_5]',
'net_access_level',
'ident_type',
'ident_type2',
+ 'locale',
'cards',
'groups'
]);
.then(function() {return patronSvc.checkAlerts()})
.then(redirectToAlertPanel)
.then(function(){
- $scope.ident_type_name = $scope.patron().ident_type().name()
+ if ($scope.patron().locale() !== null) {
+ $scope.locale_name = $scope.patron().locale().name();
+ $scope.hasLocaleName = $scope.locale_name.length > 0;
+ }
+ })
+ .then(function(){
+ $scope.ident_type_name = $scope.patron().ident_type().name();
$scope.hasIdentTypeName = $scope.ident_type_name.length > 0;
- });
+ });
} else {
// No patron, use the tab name as the page title.
egCore.strings.setPageTitle(
stat_cats : [],
stat_cat_entry_maps : {}, // cat.id to selected value
virt_id : -1, // virtual ID for new objects
+ locales : [],
init_done : false // have we loaded our initialization data?
};
service.get_perm_groups(),
service.get_perm_group_entries(),
service.get_ident_types(),
+ service.get_locales(),
service.get_org_settings(),
service.get_stat_cats(),
service.get_surveys(),
];
service.init_done = true;
}
-
return $q.all(common_data.concat(page_data));
};
}
};
+ service.get_locales = function() {
+ if (egCore.env.i18n_l) {
+ service.locales = egCore.env.i18n_l.list;
+ return $q.when();
+ } else {
+ return egCore.pcrud.retrieveAll('i18n_l', {}, {atomic : true})
+ .then(function(locales) {
+ egCore.env.absorbList(locales, 'i18n_l')
+ service.locales = locales
+ });
+ }
+ };
+
service.get_net_access_levels = function() {
if (egCore.env.cnal) {
service.net_access_levels = egCore.env.cnal.list;
service.existing_patron = current;
var patron = egCore.idl.toHash(current);
-
patron.home_ou = egCore.org.get(patron.home_ou.id);
patron.expire_date = new Date(Date.parse(patron.expire_date));
patron.dob = service.parse_dob(patron.dob);
patron.net_access_level = current.net_access_level();
patron.ident_type = current.ident_type();
patron.ident_type2 = current.ident_type2();
+ patron.locale = current.locale();
patron.groups = current.groups(); // pre-hash
angular.forEach(
user.ident_type = egCore.env.cit.map[user.ident_type];
if (user.ident_type2)
user.ident_type2 = egCore.env.cit.map[user.ident_type2];
+ if (user.locale)
+ user.locale = egCore.env.i18n_l.map[user.locale];
user.dob = service.parse_dob(user.dob);
// Clear the usrname if it looks like a UUID
patron.dob(patron.dob().toISOString().replace(/T.*/,''));
if (patron.ident_type())
patron.ident_type(patron.ident_type().id());
+ if (patron.locale())
+ patron.locale(patron.locale().code());
if (patron.net_access_level())
patron.net_access_level(patron.net_access_level().id());
$scope.edit_profiles = prs.edit_profiles;
$scope.edit_profile_entries = prs.edit_profile_entries;
$scope.ident_types = prs.ident_types;
+ $scope.locales = prs.locales;
$scope.net_access_levels = prs.net_access_levels;
$scope.user_setting_types = prs.user_setting_types;
$scope.opt_in_setting_types = prs.opt_in_setting_types;
'au.ident_type' : 3,
'au.ident_type2' : 2,
'au.photo_url' : 2,
+ 'au.locale' : 2,
'au.home_ou' : 3,
'au.profile' : 3,
'au.expire_date' : 3,
--- /dev/null
+== Localized Templates Available for Action Triggers ==
+
+This feature supplies the ability to create alternate templates for Action Triggers
+that will generate locale specific out for Action Triggers. If you send notices in
+multiple languages, we recommend putting some words to that effect in your notice
+templates. The template, message and message title can all be localized. To use the
+feature the following new UI elements have been added:
+
+- When you double-click on an Event Definition under Notifications / Action Triggers
+ to edit it there will be a tab option for Edit Alternate Template if the reactor is
+ ProcessTemplate, SendEmail or SendSMS.
+- In the Patron Registration and Patron Editor screens staff members may now select a
+ locale for a patron and edit it in the Patron Preferred Language field.
+- Patrons may set their own locale in the My Account interface off the OPAC by going to
+ Preferences -> Personal Information and setting the Preferred Language field.
+
+The templates used on the Edit Definition tab are the defaults that are used if there are
+no alternate templates available that match the preferred language. If alternate templates
+are available the system will use a locale that is an exact match and then if failing that
+use one where the language code matches and then fall back to the default one.
+
+For example, if a patron has a locale of fr-CA and there are templates for both fr-CA and
+fr-FR it will use the fr-CA. If the fr-CA template was deleted it would fall back on using
+the fr-FR for the patron since it at least shares the same base language.
+
+Valid locales are the codes defined in the i18n_locale table in the config schema.