Implemented internationalization.

Let's see how many strings have been missed.
This commit is contained in:
Erik Thuning 2024-02-26 13:10:13 +01:00
parent 5c199783c2
commit 746ad28545
12 changed files with 1251 additions and 78 deletions

@ -6,6 +6,12 @@ $db_user = 'dbname';
$db_pass = 'dbpassword';
$db_name = 'dbuser';
# Application language
$language = 'en';
# Site name
$name = 'My product tracker';
# Email subject prefix
# Will be prepended without change, so should probably end with a space
$email_subject_prefix = "System name: ";

83
html/en/base.html Normal file

@ -0,0 +1,83 @@
¤¤ head ¤¤
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./template.css" />
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="text/javascript" src="./script.js"></script>
<link rel="stylesheet" type="text/css" href="./calendar/dhtmlxcalendar.css" />
<script type="text/javascript" src="./calendar/dhtmlxcalendar.js"></script>
<title>¤title¤</title>
</head>
<body>
<div id="container">
<a class="accessibility-link"
accesskey="s"
href="#contents"
title="Skip navigation"></a>
<div id="header">
<a id="header-su-responsive"
href="https://www.su.se/english/"
title="To the Stockholm Universiy website">
<img src="images/su_logo_responsive_en.png"
alt="Stockholm University" />
</a>
<a id="header-dsv"
href="https://dsv.su.se/english/"
title="To DSV's website"
accesskey="1">
<img src="images/dsv_logo_en.png"
alt="Department of Computer and Systems Sciences" />
</a>
<a id="header-su"
href="https://www.su.se/english/"
title="To the Stockholm Universiy website">
<img src="images/su_logo_en.gif"
alt="Stockholm University" />
</a>
<div class="clear">
</div>
</div>
<div id="menu">
¤menu¤
<div class="clear">
</div>
</div>
<div id="contents">
¤¤ foot ¤¤
<div class="clear">
</div>
</div>
<div id="footer">
<div id="footer-name">
<div id="footer-dsv">
Department of Computer and Systems Sciences
</div>
<div id="footer-su">
Stockholm University
</div>
</div>
<div id="footer-contact">
<a id="footer-contact-link"
href="https://www.su.se/department-of-computer-and-systems-sciences/about-the-department/contact"
accesskey="7">
Contact
</a>
</div>
<div class="clear">
</div>
</div>
</div>
</body>
</html>
¤¤ menuitem ¤¤
<a class="item ¤align¤ ¤active¤"
href="?page=¤page¤">
¤title¤
</a>

843
html/en/fragments.html Normal file

@ -0,0 +1,843 @@
¤¤ title ¤¤
<h1>¤title¤</h1>
¤¤ subtitle ¤¤
<h2>¤title¤</h2>
¤¤ item_link ¤¤
<a href="./?page=¤page¤&action=show&id=¤id¤">¤name¤</a>
¤¤ message ¤¤
<div onClick="JavaScript:hideMessage()"
id="message"
class="¤type¤">
¤message¤
</div>
¤¤ user_table ¤¤
<form id="newloan"
class="hidden"
method="GET">
<input type="hidden"
name="action"
value="checkout" />
</form>
<table id="user-table">
<thead>
<tr>
<th>
Name
</th>
<th>
Username
</th>
<th>
Loan
</th>
<th>
</th>
</tr>
</thead>
<tbody>
¤rows¤
</tbody>
</table>
¤¤ user_row ¤¤
<tr>
<td>
¤item_link¤<span title="¤notes¤">¤has_notes¤</span>
</td>
<td>
¤name¤
</td>
<td>
¤loan¤
</td>
<td>
<button form="newloan"
name="user"
value="¤name¤">
New loan
</button>
</td>
</tr>
¤¤ product_page ¤¤
<div id="product-table">
¤product_table¤
</div>
¤¤ product_table ¤¤
<table>
<thead>
<tr>
<th class="status">
</th>
<th>
Name
</th>
<th>
Serial number
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody class="¤type¤">
¤rows¤
</tbody>
</table>
¤¤ product_row ¤¤
<tr>
<td class="status ¤status¤">
</td>
<td>
¤item_link¤
</td>
<td>
¤serial¤
</td>
<td>
¤note¤
</td>
</tr>
¤¤ product_detail_row ¤¤
<tr>
<td class="status ¤status¤">
</td>
<td colspan="3">
<dl>
¤details¤
</dl>
</td>
</tr>
¤¤ product_detail ¤¤
<dt>
¤name¤:
</dt>
<dd>
¤value¤
</dd>
¤¤ template_management ¤¤
<div>
<h2>Templates</h2>
<form class="dark templates"
onSubmit="JavaScript:loadTemplate(event)">
<datalist id="template_suggest"></datalist>
<input type="hidden"
name="page"
value="new" />
<input onFocus="JavaScript:suggest(this, 'template')"
list="template_suggest"
autocomplete="off"
type="text"
name="template"
value="¤template¤"
placeholder="Name" />
<button>
Load
</button>
<button onClick="JavaScript:saveTemplate(event)">
Save
</button>
<button onClick="JavaScript:deleteTemplate(event)">
Delete
</button>
</form>
</div>
¤¤ product_form ¤¤
<div id="product-details">
<h2>Product details</h2>
<form id="product-data"
onSubmit="JavaScript:saveProduct(event)"
class="data">
<input type="hidden"
name="id"
value="¤id¤" />
<datalist id="field_suggest"></datalist>
<datalist id="tag_suggest"></datalist>
<table>
<tfoot>
<tr>
<td>
</td>
<td>
<button id="save"
class="right">
Save
</button>
<button id="reset"
class="right">
Reset
</button>
</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>
Name:
</td>
<td>
<input type="text"
name="name"
value="¤name¤"
onFocus="JavaScript:suggestContent(this)"
list="name_suggest"
autocomplete="off" />
<datalist id="name_suggest"></datalist>
</td>
</tr>
<tr>
<td>
Manufacturer:
</td>
<td>
<input type="text"
name="brand"
value="¤brand¤"
onFocus="JavaScript:suggestContent(this)"
list="brand_suggest"
autocomplete="off" />
<datalist id="brand_suggest"></datalist>
</td>
</tr>
<tr>
<td>
Invoice number:
</td>
<td>
<input type="text"
name="invoice"
value="¤invoice¤" />
</td>
</tr>
<tr id="before_info">
<td>
Serial number:
</td>
<td>
<input type="text"
name="serial"
value="¤serial¤" />
</td>
</tr>
¤info¤
<tr>
<td>
<input onKeyPress="JavaScript:addField(event)"
onFocus="JavaScript:suggest(this, 'field')"
list="field_suggest"
autocomplete="off"
class="newfield"
type="text"
name="new_key"
placeholder="New field" />
<button class="minibutton"
onClick="addField(event)">
+
</button>
</td>
<td>
</td>
</tr>
<tr>
<td>
Tags:
</td>
<td id="tags">
¤tags¤
<input onKeyPress="JavaScript:addTag(event)"
onFocus="JavaScript:suggest(this, 'tag')"
list="tag_suggest"
autocomplete="off"
class="newtag"
type="text"
name="new_tag"
placeholder="New tag" />
<button class="minibutton"
onClick="JavaScript:addTag(event)">
+
</button>
</td>
</tr>
</tbody>
</table>
</form>
<form id="product-actions"
class="¤hidden¤">
<input type="hidden"
name="id"
value="¤id¤" />
<button onClick="JavaScript:discardProduct(event)">
Discard product
</button>
<button onClick="JavaScript:toggleService(event)">
¤service¤
</button>
</form>
</div>
<div id="product-attachments"
class="¤hidden¤">
<h2>Attachments</h2>
¤attachments¤
<form id="attachment_upload"
onSubmit="JavaScript:uploadAttachment(event)">
<input type="hidden"
name="id"
value="¤id¤" />
<input id="uploadfile"
name="uploadfile"
type="file"
onchange="showFile(event)"/>
<input id="filename"
name="filename"
type="text"
placeholder="Choose a file..."
onclick="selectFile(event)"
readonly />
<button>Upload</button>
</form>
</div>
¤¤ product_meta ¤¤
<div id="product-history">
<h2>Product history</h2>
¤history¤
</div>
<div id="product-label"
class="¤label_hidden¤">
<h2>Label</h2>
¤label¤
</div>
<div id="product-direct-checkout" class="¤checkout_hidden¤">
<h2>Check out</h2>
<form class="light"
onSubmit="JavaScript:checkoutProduct(event)">
<datalist id="user_suggest"></datalist>
<input type="hidden"
name="page"
value="checkout" />
<label for="user">Username:</label>
<input onFocus="JavaScript:suggest(this, 'user')"
type="text"
name="user"
list="user_suggest"
autocomplete="off"
placeholder="Username"
required />
<input type="hidden"
id="product"
name="product"
value="¤serial¤" />
<button>
Check out
</button>
<br/>
<label>Loan length:</label>
<button onClick="JavaScript:loanLength(event, 7, 'day')">1 week</button>
<button onClick="JavaScript:loanLength(event, 1, 'year')">1 year</button>
<button onClick="JavaScript:loanLength(event, 3, 'year')">3 years</button>
<br/>
<label for="end">End date:</label>
<input type="text"
id="end"
onClick="JavaScript:calendar(event)"
name="end"
value="¤end¤" />
</form>
</div>
¤¤ attachment_list ¤¤
<ul class="attachment-list">
¤attachments¤
</ul>
¤¤ attachment ¤¤
<li>
<strong>¤name¤</strong> (¤date¤): <a href="./?page=dl&id=¤id¤">Download</a>
<br/>
<form onSubmit="JavaScript:deleteAttachment(event)">
<input type="hidden"
name="id"
value="¤id¤" />
<input type="hidden"
name="name"
value="¤name¤" />
<button>Delete</button>
</form>
</li>
¤¤ product_label ¤¤
<div class="qr">
<a href="./?page=print&id=¤id¤"
title="Print">
<span>¤name¤</span>
<img src="./?page=qr&id=¤id¤">
<span>¤serial¤</span>
</a>
</div>
¤¤ label_page ¤¤
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./template.css" />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>¤title¤</title>
</head>
<body>
¤label¤
</body>
</html>
¤¤ info_item ¤¤
<tr>
<td>
¤name¤:
</td>
<td>
<input type="text"
class="info_item"
name="¤key¤"
value="¤value¤"
onFocus="JavaScript:suggestContent(this)"
list="¤key¤_suggest"
autocomplete="off" />
<datalist id="¤key¤_suggest"></datalist>
</td>
</tr>
¤¤ tag ¤¤
<p>
<span class="tag">
<input type="hidden"
name="tag[]"
value="¤tag¤" />
¤tag¤
<a class="tagremove"
onClick="JavaScript:removeTag(event)">
x
</a>
</span>
</p>
¤¤ user_details ¤¤
<div id="user-details">
<form onSubmit="JavaScript:updateUser(event)">
<input type="hidden"
name="id"
value="¤id¤" />
<table>
<tfoot>
<tr>
<td>
</td>
<td>
<button class="right"
id="save">
Save
</button>
<button class="right"
id="reset">
Reset
</button>
</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>
Name:
</td>
<td>
<input type="text"
value="¤displayname¤"
disabled />
</td>
</tr>
<tr>
<td>
Username:
</td>
<td>
<input type="text"
name="name"
value="¤name¤" />
</td>
</tr>
<tr>
<td>
Notes:
</td>
<td>
<textarea name="notes">¤notes¤</textarea>
</td>
</tr>
</tbody>
</table>
</form>
<form method="GET">
<input type="hidden"
name="action"
value="checkout" />
<button name="user"
value="¤name¤">
New loan
</button>
</form>
</div>
<div id="active-loans">
<h2>Current loans</h2>
¤active_loans¤
</div>
<div id="inactive-loans">
<h2>Old loans</h2>
¤inactive_loans¤
</div>
¤¤ history_table ¤¤
<table class="history">
<thead>
<tr>
<th class="status">
</th>
<th>
¤item¤
</th>
<th>
Start date
</th>
<th>
End date
</th>
<th>
Misc
</th>
</tr>
</thead>
<tbody>
¤rows¤
</tbody>
</table>
¤¤ history_row ¤¤
<tr>
<td class="status ¤status¤">
</td>
<td>
¤item_link¤
</td>
<td>
¤start_date¤
</td>
<td>
¤end_date¤
</td>
<td>
¤note¤
</td>
</tr>
¤¤ loan_extend_form ¤¤
<button onClick="JavaScript:showExtend(event)">
Extend
</button>
<form class="renew_confirm hidden"
onSubmit="JavaScript:extendLoan(event)">
<input type="hidden"
name="product"
value="¤id¤" />
<input onClick="JavaScript:calendar(event)"
id="¤id¤_date"
class="narrow"
type="text"
name="end"
value="¤end_new¤" />
<button>
Save
</button>
</form>
¤¤ checkout_page ¤¤
<div id="user-select">
<h2>Choose borrower</h2>
<form class="dark"
action="./"
method="GET">
<datalist id="user_suggest"></datalist>
<input type="hidden"
name="page"
value="checkout" />
<div>
<label for="user">
Username:
</label>
<input onFocus="JavaScript:suggest(this, 'user')"
type="text"
name="user"
id="user"
list="user_suggest"
autocomplete="off"
placeholder="Username"
value="¤user¤" />
<button type="submit" >
Find
</button>
</div>
<div>
<label for="email">
E-mail:
</label>
<input type="text"
name="email"
id="email"
autocomplete="off"
placeholder="E-mail"
value="¤email¤" />
</div>
<div>
<label for="displayname">
Name:
</label>
<input type="text"
name="displayname"
id="displayname"
value="¤displayname¤"
disabled />
</div>
<div>
<label for="notes">
Notes:
</label>
<textarea name="notes"
id="notes"
disabled>¤notes¤</textarea>
</div>
</form>
</div>
<div id="product-checkout">
<h2>Check out product</h2>
<form class="light"
onSubmit="JavaScript:checkoutProduct(event)">
<input type="hidden"
name="page"
value="checkout" />
<input type="hidden"
name="user"
value="¤user¤">
<label for="product">Product:</label>
<input type="text"
id="product"
name="product"
placeholder="Serial number"
required
¤disabled¤ />
<button>
Check out
</button>
<br/>
<label>Loan length:</label>
<button onClick="JavaScript:loanLength(event, 7, 'day')">1 week</button>
<button onClick="JavaScript:loanLength(event, 1, 'year')">1 year</button>
<button onClick="JavaScript:loanLength(event, 3, 'year')">3 years</button>
<br/>
<label for="end">End date:</label>
<input type="text"
id="end"
onClick="JavaScript:calendar(event)"
name="end"
value="¤end¤"
¤disabled¤ />
</form>
¤subtitle¤
¤loan_table¤
</div>
¤¤ inventory_start ¤¤
<form class="dark"
onSubmit="JavaScript:startInventory(event)">
<button name="start">
Start inventory
</button>
</form>
¤¤ inventory_do ¤¤
<div id="inventory-overview"
class="dark">
<span class="label">
Start date:
</span>
¤start_date¤
<br/>
<span class="label">
Total number of products:
</span>
¤total_count¤
<br/>
<span class="label">
Number of registered products:
</span>
<span id="seen_count">
¤seen_count¤
</span>
<form class="dark ¤hide¤"
onSubmit="JavaScript:endInventory(event)">
<button name="end">
End inventory
</button>
</form>
</div>
<form id="inventory-register"
class="light ¤hide¤"
onSubmit="JavaScript:inventoryProduct(event)">
<label for="serial">
Product:
</label>
<input type="text"
name="serial"
id="serial"
placeholder="Serial number" />
<button>
Register
</button>
</form>
<div id="unseen-products">
<h2>¤unseen_title¤</h2>
¤unseen¤
</div>
<div id="seen-products">
<h2>Registered products</h2>
¤seen¤
</div>
¤¤ return_page ¤¤
<form class="dark"
onSubmit="JavaScript:returnProduct(event)">
<label for="serial">
Product:
</label>
<input type="text"
name="serial"
id="serial"
placeholder="Serial number"
required />
<button>
Return
</button>
</form>
¤¤ inventory_table ¤¤
<table id="inventory-history">
<thead>
<tr>
<th>
</th>
<th>
¤item¤
</th>
<th>
Start date
</th>
<th>
End date
</th>
<th>
Registered products
</th>
<th>
Missing products
</th>
</tr>
</thead>
<tbody>
¤rows¤
</tbody>
</table>
¤¤ inventory_row ¤¤
<tr>
<td>
</td>
<td>
¤item_link¤
</td>
<td>
¤start_date¤
</td>
<td>
¤end_date¤
</td>
<td>
¤num_seen¤
</td>
<td>
¤num_unseen¤
</td>
</tr>
¤¤ search_form ¤¤
<form onSubmit="JavaScript:doSearch(event)"
id="search"
class="dark">
<p>
<label for="q">Search term:</label>
<input type="hidden"
name="page"
value="search" />
<input type="text"
onKeyPress="JavaScript:searchInput(event)"
name="q"
id="q"
placeholder="What are you looking for?"
value=""
autofocus />
<button type="submit">
Search
</button>
</p>
<div id="terms">
¤terms¤
</div>
<div class="clear"></div>
</form>
<p id="hints">
Advanced searches are possible by prefixing a search term with a keyword ending with a colon, for example: <strong>serial:123456</strong> or <strong>tag:computer</tag>
</p>
<div id="found-products"
class="¤hidden¤">
<h2>Products</h2>
¤product_results¤
</div>
<div id="found-users"
class="¤hidden¤">
<h2>Users</h2>
¤user_results¤
</div>
¤¤ search_term ¤¤
<p class="left">
<span class="term">
<input type="hidden"
name="¤key¤"
value="¤value¤" />
¤term¤
<a class="termremove"
onClick="JavaScript:removeTerm(event)">
x
</a>
</span>
</p>

@ -14,15 +14,15 @@ class HistoryPage extends Page {
} catch(Exception $e) {
$this->inventory = null;
$this->action = 'list';
$this->error = 'Det finns ingen inventering med det ID-numret.';
$this->error = i18n('There is no inventory with that ID.');
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Inventeringsdetaljer';
$this->subtitle = i18n('Inventory details');
break;
case 'list':
$this->subtitle = 'Historik';
$this->subtitle = i18n('History');
break;
}
}
@ -30,16 +30,16 @@ class HistoryPage extends Page {
protected function render_body() {
switch($this->action) {
case 'list':
print(replace(array('title' => 'Genomförda inventeringar'),
print(replace(array('title' => i18n('Past inventories')),
$this->fragments['subtitle']));
print($this->build_inventory_table());
print(replace(array('title' => 'Skrotade artiklar'),
print(replace(array('title' => i18n('Discarded products')),
$this->fragments['subtitle']));
$discards = get_items('product_discarded');
if($discards) {
print($this->build_product_table($discards));
} else {
print('Inga artiklar skrotade.');
print(i18n('No products discarded.'));
}
break;
case 'show':
@ -55,7 +55,7 @@ class HistoryPage extends Page {
private function build_inventory_table() {
$items = get_items('inventory_old');
if(!$items) {
return 'Inga inventeringar gjorda.';
return i18n('No inventories have been performed.');
}
$rows = '';
foreach($items as $inventory) {
@ -75,7 +75,7 @@ class HistoryPage extends Page {
'num_unseen' => $num_unseen),
$this->fragments['inventory_row']);
}
return replace(array('item' => 'Tillfälle',
return replace(array('item' => i18n('Number'),
'rows' => $rows),
$this->fragments['inventory_table']);
}

@ -3,22 +3,26 @@ abstract class Page extends Responder {
protected abstract function render_body();
protected $page = 'checkout';
protected $title = "DSV Utlåning";
protected $title = '';
protected $subtitle = '';
protected $error = null;
protected $menuitems = array('checkout' => 'Låna',
'return' => 'Lämna',
'products' => 'Artiklar',
'new' => 'Ny artikel',
'users' => 'Låntagare',
'inventory' => 'Inventera',
'history' => 'Historik',
'search' => 'Sök');
protected $menuitems = array();
private $template_parts = array();
public function __construct() {
global $language, $name;
parent::__construct();
$this->template_parts = get_fragments('./html/base.html');
$this->title = $name;
$this->menuitems = array('checkout' => i18n('Check out'),
'return' => i18n('Return'),
'products' => i18n('Products'),
'new' => i18n('New product'),
'users' => i18n('Borrowers'),
'inventory' => i18n('Inventory'),
'history' => i18n('History'),
'search' => i18n('Search'));
$this->template_parts = get_fragments("./html/$language/base.html");
if(isset($_GET['page'])) {
$this->page = $_GET['page'];
@ -118,7 +122,7 @@ abstract class Page extends Responder {
$loan_str = $product->get_name();
break;
default:
$loan_str = $count .' artiklar';
$loan_str = i18n('{count} products', $count);
break;
}
$replacements['loan'] = $loan_str;
@ -143,35 +147,31 @@ abstract class Page extends Responder {
'name' => $product->get_name(),
'page' => 'products'),
$this->fragments['item_link']);
$note = 'Tillgänglig';
$note = i18n('Available');
$status = $product->get_status();
switch($status) {
case 'discarded':
$discarded = format_date($product->get_discardtime());
$note = 'Skrotad '.$discarded;
break;
case 'service':
$service = $product->get_active_service();
$note = 'På service sedan '
.format_date($service->get_starttime());
break;
case 'on_loan':
case 'overdue':
$loan = $product->get_active_loan();
$user = $loan->get_user();
$replacements = array('name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users');
$userlink = replace($replacements,
$this->fragments['item_link']);
$note = 'Utlånad till '.$userlink;
if($loan->is_overdue()) {
$note .= ', försenad';
} else {
$note .= ', slutdatum '
.format_date($loan->get_endtime());
}
break;
case 'discarded':
$discarded = format_date($product->get_discardtime());
$note = i18n('Discarded on {date}', $discarded);
break;
case 'service':
$service = $product->get_active_service();
$note = i18n('Being serviced since {date}',
$service->get_starttime());
break;
case 'on_loan':
case 'overdue':
$loan = $product->get_active_loan();
$user = $loan->get_user();
$replacements = array(
'name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users'
);
$userlink = replace($replacements,
$this->fragments['item_link']);
$note = i18n('Borrowed by {user} {loan}', $userlink, $loan);
break;
}
$out = replace(array('status' => $status,
'item_link' => $prodlink,
@ -229,7 +229,7 @@ abstract class Page extends Responder {
$this->fragments['history_row']);
}
return replace(array('rows' => $rows,
'item' => 'Artikel'),
'item' => i18n('Product')),
$this->fragments['history_table']);
}
@ -249,34 +249,35 @@ abstract class Page extends Responder {
$discardtime = $product->get_discardtime();
if($discardtime && $discardtime < $regtime) {
$status = 'discarded';
$note = 'Skrotad '.format_date($discardtime);
$note = i18n('Discarded on {date}',
format_date($discardtime));
} else {
$status = 'available';
}
} else if($event instanceof Service) {
$status = 'service';
$note = 'På service sedan '.format_date($event->get_starttime());
$starttime = $event->get_starttime();
$returntime = $event->get_returntime();
$note = i18n('Being serviced since {date}', $starttime);
if($returntime) {
$note .= ', åter den '.format_date($returntime);
$note = i18n("Serviced between {start} and {end}",
$starttime,
$returntime);
}
} else if($event instanceof Loan) {
$user = $event->get_user();
$userlink = replace(array('name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users'),
$this->fragments['item_link']);
$userlink = replace(
array('name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users'),
$this->fragments['item_link']);
$status = 'on_loan';
$note = 'Utlånad till '.$userlink;
$returntime = $event->get_returntime();
$note = i18n('Borrowed by {user} {loan} {inventorytime}',
$userlink,
$event,
$regtime);
if($event->get_endtime() < $regtime) {
$status = 'overdue';
$note .= ', försenad';
} else {
$note .= ', slutdatum '.format_date($event->get_endtime());
}
if($returntime) {
$note .= ', återlämnad '.format_date($returntime);
}
}
$rows .= replace(array('status' => $status,
@ -302,17 +303,17 @@ abstract class Page extends Responder {
$unseen[] = $product;
}
}
$missing = 'Saknade artiklar';
$missing = i18n('Missing products');
$hidden = 'hidden';
if($interactive) {
$missing = 'Kvarvarande artiklar';
$missing = i18n('Remaining products');
$hidden = '';
}
$unseen_table = 'Inga artiklar saknas.';
$unseen_table = i18n('No products are missing.');
if($unseen) {
$unseen_table = $this->build_product_table($unseen);
}
$seen_table = 'Inga artiklar inventerade.';
$seen_table = i18n('No products registered');
if($seen) {
$seen_table = $this->build_seen_table($seen, $inventory);
}

@ -16,16 +16,16 @@ class ProductPage extends Page {
} catch(Exception $e) {
$this->action = 'list';
$this->product = null;
$this->error = 'Det finns ingen artikel med det ID-numret.';
$this->error = i18n('There is no product with that ID.');
}
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Artikeldetaljer';
$this->subtitle = i18n('Product details');
break;
case 'list':
$this->subtitle = 'Artikellista';
$this->subtitle = i18n('Product list');
break;
}
}
@ -70,7 +70,7 @@ class ProductPage extends Page {
'label_hidden' => 'hidden',
'checkout_hidden' => 'hidden',
'hidden' => '',
'service' => 'Starta service',
'service' => i18n('Start service'),
'history' => $history,
'attachments' => $attachments,
'end' => format_date(default_loan_end(time())));
@ -81,7 +81,7 @@ class ProductPage extends Page {
if(!$this->product->get_discardtime()) {
$fields['label_hidden'] = '';
if($this->product->get_status() == 'service') {
$fields['service'] = 'Avsluta service';
$fields['service'] = i18n('End service');
}
if($this->product->get_status() == 'available') {
$fields['checkout_hidden'] = '';
@ -94,7 +94,7 @@ class ProductPage extends Page {
private function build_history_table($history) {
if(!$history) {
return 'Ingen historik att visa.';
return i18n('No history to display.');
}
$rows = '';
foreach($history as $event) {
@ -126,14 +126,14 @@ class ProductPage extends Page {
$this->fragments['history_row']);
}
return replace(array('rows' => $rows,
'item' => 'Låntagare'),
'item' => i18n('Borrower')),
$this->fragments['history_table']);
}
private function build_attachment_list($attachments) {
if(!$attachments) {
return '<p>Inga bilagor.</p>';
return '<p>'.i18n('No attachments.').'</p>';
}
$items = '';
foreach($attachments as $attachment) {

@ -4,7 +4,8 @@ abstract class Responder {
protected $ldap = null;
public function __construct() {
$this->fragments = get_fragments('./html/fragments.html');
global $language;
$this->fragments = get_fragments("./html/$language/fragments.html");
$this->ldap = new Ldap();
}

@ -16,16 +16,16 @@ class UserPage extends Page {
} catch(Exception $e) {
$this->user = null;
$this->action = 'list';
$this->error = 'Det finns ingen användare med det ID-numret.';
$this->error = i18n('There is no user with that ID.');
}
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Låntagardetaljer';
$this->subtitle = i18n('Borrower details');
break;
case 'list':
$this->subtitle = 'Låntagarlista';
$this->subtitle = i18n('Borrower list');
break;
}
}

@ -1,5 +1,7 @@
<?php
require_once('./include/translations.php');
/*
Takes an html file containing named fragments.
Returns an associative array on the format array[name]=>fragment.
@ -43,6 +45,11 @@ function get_fragments($infile) {
return try_adding($name, $current_fragment, $out, $infile);
}
function i18n($string, ...$args) {
global $language, $i18n;
return $i18n[$string][$language](...$args);
}
function try_adding($key, $value, $array, $filename) {
if(array_key_exists($key, $array)) {
throw new Exception('There is already a fragment with that name in '.$filename);

232
include/translations.php Normal file

@ -0,0 +1,232 @@
<?php
$i18n = array(
"Check out" => array(
"en" => function() { return "Check out"; },
"sv" => function() { return "Låna"; },
),
"Return" => array(
"en" => function() { return "Return"; },
"sv" => function() { return "Lämna"; },
),
"Products" => array(
"en" => function() { return "Products"; },
"sv" => function() { return "Artiklar"; },
),
"Product" => array(
"en" => function() { return "Product"; },
"sv" => function() { return "Artikel"; },
),
"New product" => array(
"en" => function() { return "New product"; },
"sv" => function() { return "Ny artikel"; },
),
"Borrowers" => array(
"en" => function() { return "Borrowers"; },
"sv" => function() { return "Låntagare"; },
),
"Borrower" => array(
"en" => function() { return "Borrower"; },
"sv" => function() { return "Låntagare"; },
),
"Inventory" => array(
"en" => function() { return "Inventory"; },
"sv" => function() { return "Inventera"; },
),
"History" => array(
"en" => function() { return "History"; },
"sv" => function() { return "Historik"; },
),
"Search" => array(
"en" => function() { return "Search"; },
"sv" => function() { return "Sök"; },
),
"{count} products" => array(
"en" => function($count) { return "$count products"; },
"sv" => function($count) { return "$count artiklar"; },
),
"Available" => array(
"en" => function() { return "Available"; },
"sv" => function() { return "Tillgänglig"; },
),
"Discarded on {date}" => array(
"en" => function($date) { return "Discarded on $date"; },
"sv" => function($date) { return "Skrotad $date"; },
),
"Being serviced since {date}" => array(
"en" => function($date) {
$date = format_date($date);
return "Being serviced since $date";
},
"sv" => function($date) {
$date = format_date($date);
return "På service sedan $date";
},
),
"Serviced between {start} and {end}" => array(
"en" => function($start, $end) {
$start = format_date($start);
$end = format_date($end);
return "Serviced between $start and $end";
},
"sv" => function($start, $end) {
$start = format_date($start);
$end = format_date($end);
return "På service mellan $start och $end";
},
),
"Borrowed by {user} {loan}" => array(
"en" => function($user, $loan) {
$note = "Borrowed by $user";
if($loan->is_overdue()) {
$note .= ', overdue';
} else {
$note .= ', end date '.format_date($loan->get_endtime());
}
return $note;
},
"sv" => function($user, $loan) {
$note = "Utlånad till $user";
if($loan->is_overdue()) {
$note .= ', försenad';
} else {
$note .= ', slutdatum '.format_date($loan->get_endtime());
}
return $note;
},
),
"Borrowed by {user} {loan} {inventorytime}" => array(
"en" => function($user, $loan, $inventorytime) {
$note = "Borrowed by $user";
$endtime = $loan->get_endtime();
$returntime = $loan->get_returntime();
if($endtime < $inventorytime) {
$note .= ', overdue';
} else {
$note .= ', end date '.format_date($endtime);
}
if($returntime) {
$note .= ', returned on '.format_date($returntime);
}
return $note;
},
"sv" => function($user, $loan, $inventorytime) {
$note = "Utlånad till $user";
$endtime = $loan->get_endtime();
$returntime = $loan->get_returntime();
if($endtime < $inventorytime) {
$note .= ', försenad';
} else {
$note .= ', slutdatum '.format_date($endtime);
}
if($returntime) {
$note .= ', återlämnad '.format_date($returntime);
}
return $note;
},
),
"Missing products" => array(
"en" => function() { return "Missing products"; },
"sv" => function() { return "Saknade artiklar"; },
),
"Remaining products" => array(
"en" => function() { return "Remaining products"; },
"sv" => function() { return "Kvarvarande artiklar"; },
),
"No products are missing." => array(
"en" => function() { return "No products are missing."; },
"sv" => function() { return "Inga artiklar saknas."; },
),
"No products registered" => array(
"en" => function() { return "No products registered"; },
"sv" => function() { return "Inga artiklar inventerade."; },
),
"There is no product with that ID." => array(
"en" => function() {
return "There is no product with that ID.";
},
"sv" => function() {
return "Det finns ingen artikel med det ID-numret.";
},
),
"Product details" => array(
"en" => function() { return "Product details"; },
"sv" => function() { return "Artikeldetaljer"; },
),
"Product list" => array(
"en" => function() { return "Product list"; },
"sv" => function() { return "Artikellista"; },
),
"Start service" => array(
"en" => function() { return "Start service"; },
"sv" => function() { return "Starta service"; },
),
"End service" => array(
"en" => function() { return "End service"; },
"sv" => function() { return "Avsluta service"; },
),
"No history to display." => array(
"en" => function() { return "No history to display."; },
"sv" => function() { return "Ingen historik att visa."; },
),
"No attachments." => array(
"en" => function() { return "No attachments."; },
"sv" => function() { return "Inga bilagor."; },
),
"Inventory details" => array(
"en" => function() { return "Inventory details"; },
"sv" => function() { return "Inventeringsdetaljer"; },
),
"There is no inventory with that ID." => array(
"en" => function() {
return "There is no inventory with that ID.";
},
"sv" => function() {
return "Det finns ingen inventering med det ID-numret.";
},
),
"Past inventories" => array(
"en" => function() { return "Past inventories"; },
"sv" => function() { return "Genomförda inventeringar"; },
),
"Discarded products" => array(
"en" => function() { return "Discarded products"; },
"sv" => function() { return "Skrotade artiklar"; },
),
"No products discarded." => array(
"en" => function() { return "No products discarded."; },
"sv" => function() { return "Inga artiklar skrotade."; },
),
"No inventories have been performed." => array(
"en" => function() { return "No inventories have been performed."; },
"sv" => function() { return "Inga inventeringar gjorda."; },
),
"Number" => array(
"en" => function() { return "Number"; },
"sv" => function() { return "Nummer"; },
),
"There is no user with that ID." => array(
"en" => function() {
return "There is no user with that ID.";
},
"sv" => function() {
return "Det finns ingen användare med det ID-numret.";
},
),
"Borrower list" => array(
"en" => function() { return "Borrower list"; },
"sv" => function() { return "Låntagarlista"; },
),
"Borrower details" => array(
"en" => function() { return "Borrower details"; },
"sv" => function() { return "Låntagardetaljer"; },
),
/*
"" => array(
"en" => function() { return ""; },
"sv" => function() { return ""; },
),
*/
);
?>