1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 09:23:13 +01:00

merge localization

This commit is contained in:
Tim Daubenschütz 2015-06-02 15:38:54 +02:00
commit 0ef0558e3f
18 changed files with 205 additions and 131 deletions

View File

@ -15,7 +15,7 @@ class PieceActions {
this.actions.updatePiece(res.piece);
})
.catch((err) => {
console.log(err);
console.log(err);
});
}
}

View File

@ -15,7 +15,6 @@ class PieceListActions {
PieceListFetcher
.fetch(page, pageSize, search, orderBy, orderAsc)
.then((res) => {
console.log(res);
this.actions.updatePieceList({
page,
pageSize,

View File

@ -2,6 +2,7 @@ import React from 'react';
import AccordionListItemTable from './accordion_list_item_table';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItem = React.createClass({
propTypes: {
@ -19,7 +20,7 @@ let AccordionListItem = React.createClass({
</div>
<div className="info-wrapper">
<h1>{this.props.content.title}</h1>
<h3>by {this.props.content.artist_name}</h3>
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
</div>
<span style={{'clear': 'both'}}></span>
</div>

View File

@ -5,6 +5,8 @@ import TableItem from '../ascribe_table/table_item';
import TableColumnContentModel from '../../models/table_column_content_model';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemTable = React.createClass({
propTypes: {
className: React.PropTypes.string,

View File

@ -15,6 +15,8 @@ import TableItemText from '../ascribe_table/table_item_text';
import TableItemCheckbox from '../ascribe_table/table_item_checkbox';
import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemTableEditions = React.createClass({
propTypes: {
@ -52,9 +54,9 @@ let AccordionListItemTableEditions = React.createClass({
render() {
let columnList = [
new TableColumnContentModel((item) => { return { 'editionId': item.id, 'pieceId': this.props.parentId, 'selectItem': this.selectItem, 'selected': item.selected }}, '', '', TableItemCheckbox, 1, false),
new TableColumnContentModel((item) => { return { 'content': item.edition_number }}, 'num_editions', 'Nr', TableItemText, 1, false),
new TableColumnContentModel((item) => { return { 'content': item.bitcoin_id }}, 'bitcoin_id', 'Bitcoin Address', TableItemText, 5, false),
new TableColumnContentModel((item) => { return { 'content': item.acl }}, 'acl', 'Actions', TableItemAclFiltered, 4, false)
new TableColumnContentModel((item) => { return { 'content': item.edition_number }}, 'num_editions', '#', TableItemText, 1, false),
new TableColumnContentModel((item) => { return { 'content': item.bitcoin_id }}, 'bitcoin_id', getLangText('Bitcoin Address'), TableItemText, 5, false),
new TableColumnContentModel((item) => { return { 'content': item.acl }}, 'acl', getLangText('Actions'), TableItemAclFiltered, 4, false)
];
return (

View File

@ -1,6 +1,8 @@
import React from 'react';
import Router from 'react-router';
import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link;
let PaginationButton = React.createClass({
@ -25,14 +27,14 @@ let PaginationButton = React.createClass({
page -= 1;
directionDisplay = (
<span>
<span aria-hidden="true">&larr;</span> Previous
<span aria-hidden="true">&larr;</span> {getLangText('Previous')}
</span>
);
} else {
page += 1;
directionDisplay = (
<span>
Next <span aria-hidden="true">&rarr;</span>
{getLangText('Next')} <span aria-hidden="true">&rarr;</span>
</span>
);
}

View File

@ -1,7 +1,6 @@
import React from 'react';
import TableColumnMixin from '../../mixins/table_column_mixin';
import GeneralUtils from '../../utils/general_utils';
import TableHeaderItem from './table_header_item';
import TableColumnContentModel from '../../models/table_column_content_model';

View File

@ -5,6 +5,14 @@ import AltContainer from 'alt/AltContainer';
import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store';
import Nav from 'react-bootstrap/lib/Nav';
import Navbar from 'react-bootstrap/lib/Navbar';
import NavItem from 'react-bootstrap/lib/NavItem';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import { getLangText } from '../utils/lang_utils';
let Link = Router.Link;
let Header = React.createClass({
@ -24,40 +32,24 @@ let Header = React.createClass({
render() {
return (
<nav className="navbar navbar-default">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand" href="#">
<span>ascribe </span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</a>
</div>
<div id="navbar" className="navbar-collapse collapse">
<ul className="nav navbar-nav navbar-left">
</ul>
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{this.state.currentUser.username} <span className="caret"></span></a>
<ul className="dropdown-menu" role="menu">
<li><a href="/art/faq/">Account Settings</a></li>
<li className="divider"></li>
<li><a href="/art/faq/">FAQ</a></li>
<li><a href="/art/terms/">Terms of Service</a></li>
<li className="divider"></li>
<li><a href="/api/users/logout/">Log out</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<Navbar>
<Nav>
<a className="navbar-brand" href="#">
<span>ascribe </span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</a>
</Nav>
<Nav right>
<DropdownButton eventKey={3} title={this.state.currentUser.username}>
<MenuItem eventKey="1" href="/art/account_settings/">{getLangText('Account Settings')}</MenuItem>
<li className="divider"></li>
<MenuItem eventKey="2" href="/art/faq/">{getLangText('FAQ')}</MenuItem>
<MenuItem eventKey="3" href="/art/terms/">{getLangText('Terms of Service')}</MenuItem>
<MenuItem divider />
<MenuItem eventKey="4" href="/api/users/logout/">{getLangText('Log out')}</MenuItem>
</DropdownButton>
</Nav>
</Navbar>
);
}
});

32
js/constants/languages.js Normal file
View File

@ -0,0 +1,32 @@
const languages = {
'en-US': {
'Bitcoin Address': 'Bitcoin Address',
'Actions': 'Actions',
'Hide': 'Hide',
'Show the edition': 'Show the edition',
'Show all %d Editions': 'Show all %d Editions',
'by %s': 'by %s',
'Account Settings': 'Account Settings',
'FAQ': 'FAQ',
'Terms of Service': 'Terms of Service',
'Log out': 'Log out',
'Previous': 'Previous',
'Next': 'Next'
},
'de': {
'Bitcoin Address': 'Bitcoin Adresse',
'Actions': 'Aktionen',
'Hide': 'Verstecke',
'Show the edition': 'Zeige die Edition',
'Show all %d Editions': 'Zeige alle %d Editionen an',
'by %s': 'von %s',
'Account Settings': 'Kontoeinstellungen',
'FAQ': 'Fragen & Antworten',
'Terms of Service': 'AGB',
'Log out': 'Log out',
'Previous': 'Zurück',
'Next': 'Weiter'
}
};
export default languages;

View File

@ -1,7 +1,6 @@
import fetch from '../utils/fetch';
import AppConstants from '../constants/application_constants';
import FetchApiUtils from '../utils/fetch_api_utils';
let EditionFetcher = {

View File

@ -1,5 +1,5 @@
import AppConstants from '../constants/application_constants';
import FetchApiUtils from '../utils/fetch_api_utils';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
import fetch from '../utils/fetch';
@ -9,7 +9,7 @@ let PieceListFetcher = {
* Can be called with all supplied queryparams the API.
*/
fetch(page, pageSize, search, orderBy, orderAsc) {
let ordering = FetchApiUtils.generateOrderingQueryParams(orderBy, orderAsc);
let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return fetch.get('pieces_list', { page, pageSize, search, ordering });
}
};

View File

@ -1,7 +1,6 @@
import fetch from '../utils/fetch';
import AppConstants from '../constants/application_constants';
import FetchApiUtils from '../utils/fetch_api_utils';
let UserFetcher = {

View File

@ -1,6 +1,6 @@
import React from 'react';
import GeneralUtils from '../utils/general_utils';
import { sumNumList } from '../utils/general_utils';
let TableColumnMixin = {
/**
@ -11,7 +11,7 @@ let TableColumnMixin = {
let bootstrapClasses = ['col-xs-', 'col-sm-', 'col-md-', 'col-lg-'];
let listOfRowValues = list.map((column) => column.rowWidth );
let numOfUsedColumns = GeneralUtils.sumNumList(listOfRowValues);
let numOfUsedColumns = sumNumList(listOfRowValues);
if(numOfUsedColumns > numOfColumns) {
throw new Error('This table has only ' + numOfColumns + ' columns to assign. You defined ' + numOfUsedColumns + '. Change this in the columnMap you\'re passing to the table.')

View File

@ -1,11 +1,11 @@
import alt from '../alt';
import EditionAction from '../actions/edition_actions';
import EditionActions from '../actions/edition_actions';
class EditionStore {
constructor() {
this.edition = {};
this.bindActions(EditionAction);
this.bindActions(EditionActions);
}
onUpdateEdition(edition) {

View File

@ -1,5 +1,5 @@
import { default as _fetch } from 'isomorphic-fetch';
import FetchApiUtils from '../utils/fetch_api_utils';
import { argsToQueryParams } from '../utils/fetch_api_utils';
class UrlMapError extends Error {};
@ -55,7 +55,7 @@ class Fetch {
prepareUrl(url, params, attachParamsToQuery) {
let newUrl = this.getUrl(url);
let re = /\${(\w+)}/g;
newUrl = newUrl.replace(re, (match, key) => {
let val = params[key]
if (!val) {
@ -66,7 +66,7 @@ class Fetch {
});
if (attachParamsToQuery && params && Object.keys(params).length > 0) {
newUrl += FetchApiUtils.argsToQueryParams(params);
newUrl += argsToQueryParams(params);
}
return newUrl;

View File

@ -1,8 +1,6 @@
import GeneralUtils from './general_utils';
import { sanitize } from './general_utils';
// TODO: Create Unittests that test all functions
let FetchApiUtils = {
/**
* Takes a key-value object of this form:
@ -20,48 +18,45 @@ let FetchApiUtils = {
* CamelCase gets converted to snake_case!
*
*/
argsToQueryParams(obj) {
export function argsToQueryParams(obj) {
obj = GeneralUtils.sanitize(obj);
obj = sanitize(obj);
return Object
.keys(obj)
.map((key, i) => {
let s = '';
return Object
.keys(obj)
.map((key, i) => {
let s = '';
if(i === 0) {
s += '?';
} else {
s += '&';
}
if(i === 0) {
s += '?';
} else {
s += '&';
}
let snakeCaseKey = key.replace(/[A-Z]/, (match) => '_' + match.toLowerCase());
let snakeCaseKey = key.replace(/[A-Z]/, (match) => '_' + match.toLowerCase());
return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]);
})
.join('');
},
/**
* Takes a string and a boolean and generates a string query parameter for
* an API call.
*/
generateOrderingQueryParams(orderBy, orderAsc) {
let interpolation = '';
if(!orderAsc) {
interpolation += '-';
}
return interpolation + orderBy;
},
status(response) {
if (response.status >= 200 && response.status < 300) {
return response
}
throw new Error(response.json())
}
return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]);
})
.join('');
};
export default FetchApiUtils;
/**
* Takes a string and a boolean and generates a string query parameter for
* an API call.
*/
export function generateOrderingQueryParams(orderBy, orderAsc) {
let interpolation = '';
if(!orderAsc) {
interpolation += '-';
}
return interpolation + orderBy;
};
export function status(response) {
if (response.status >= 200 && response.status < 300) {
return response
}
throw new Error(response.json())
};

View File

@ -1,41 +1,63 @@
// TODO: Create Unittests that test all functions
let GeneralUtils = {
/**
* Removes undefined and null values from an key-value object.
*/
sanitize(obj) {
Object
.keys(obj)
.map((key) => {
// By matching null with a double equal, we can match undefined and null
// http://stackoverflow.com/a/15992131
if(obj[key] == null || obj[key] === '') {
delete obj[key];
}
});
return obj;
},
/**
* Returns the values of an object.
*/
valuesOfObject(obj) {
return Object
.keys(obj)
.map(key => obj[key]);
},
/**
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
*/
sumNumList(l) {
let sum = 0;
l.forEach((num) => sum += parseFloat(num) || 0);
return sum;
}
export function sanitize(obj) {
Object
.keys(obj)
.map((key) => {
// By matching null with a double equal, we can match undefined and null
// http://stackoverflow.com/a/15992131
if(obj[key] == null || obj[key] === '') {
delete obj[key];
}
});
return obj;
};
export default GeneralUtils;
/**
* Returns the values of an object.
*/
export function valuesOfObject(obj) {
return Object
.keys(obj)
.map(key => obj[key]);
};
/**
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
*/
export function sumNumList(l) {
let sum = 0;
l.forEach((num) => sum += parseFloat(num) || 0);
return sum;
};
/*
Taken from http://stackoverflow.com/a/4795914/1263876
Behaves like C's format string function
*/
export function formatText() {
var args = arguments,
string = args[0],
i = 1;
return string.replace(/%((%)|s|d)/g, function (m) {
// m is the matched format, e.g. %s, %d
var val = null;
if (m[2]) {
val = m[2];
} else {
val = args[i];
// A switch statement so that the formatter can be extended. Default is %s
switch (m) {
case '%d':
val = parseFloat(val);
if (isNaN(val)) {
val = 0;
}
break;
}
i++;
}
return val;
});
};

30
js/utils/lang_utils.js Normal file
View File

@ -0,0 +1,30 @@
import languages from '../constants/languages';
import { formatText } from './general_utils';
/**
* Is used to translate strings to another language. Basically can be used with C's string format method.
* @param {string} s The string you want to translate
* @param {array} args An array of arguments (essentially JavaScript's this.arguments) that can be used to substitute digits and other strings
* @return {string} The formated string
*/
export function getLangText(s, ...args) {
let lang = navigator.language || navigator.userLanguage;
// this is just for testing, as changing the navigator.language wasn't possible
lang = 'de';
try {
if(lang in languages) {
return formatText(languages[lang][s], args);
} else {
// just use the english language
return formatText(languages['en-US'][s], args);
}
} catch(err) {
if(!(s in languages[lang])) {
console.error(new Error('Language-string is not in constants file. Add: "' + s + '" to the "' + lang + '" language file.'));
} else {
console.error(err);
}
}
};