1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +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,6 @@ class PieceListActions {
PieceListFetcher PieceListFetcher
.fetch(page, pageSize, search, orderBy, orderAsc) .fetch(page, pageSize, search, orderBy, orderAsc)
.then((res) => { .then((res) => {
console.log(res);
this.actions.updatePieceList({ this.actions.updatePieceList({
page, page,
pageSize, pageSize,

View File

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

View File

@ -5,6 +5,8 @@ import TableItem from '../ascribe_table/table_item';
import TableColumnContentModel from '../../models/table_column_content_model'; import TableColumnContentModel from '../../models/table_column_content_model';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemTable = React.createClass({ let AccordionListItemTable = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, 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 TableItemCheckbox from '../ascribe_table/table_item_checkbox';
import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered'; import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemTableEditions = React.createClass({ let AccordionListItemTableEditions = React.createClass({
propTypes: { propTypes: {
@ -52,9 +54,9 @@ let AccordionListItemTableEditions = React.createClass({
render() { render() {
let columnList = [ 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 { '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.edition_number }}, 'num_editions', '#', TableItemText, 1, false),
new TableColumnContentModel((item) => { return { 'content': item.bitcoin_id }}, 'bitcoin_id', 'Bitcoin Address', TableItemText, 5, 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', 'Actions', TableItemAclFiltered, 4, false) new TableColumnContentModel((item) => { return { 'content': item.acl }}, 'acl', getLangText('Actions'), TableItemAclFiltered, 4, false)
]; ];
return ( return (

View File

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

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import TableColumnMixin from '../../mixins/table_column_mixin'; import TableColumnMixin from '../../mixins/table_column_mixin';
import GeneralUtils from '../../utils/general_utils';
import TableHeaderItem from './table_header_item'; import TableHeaderItem from './table_header_item';
import TableColumnContentModel from '../../models/table_column_content_model'; 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 UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store'; 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 Link = Router.Link;
let Header = React.createClass({ let Header = React.createClass({
@ -24,40 +32,24 @@ let Header = React.createClass({
render() { render() {
return ( return (
<nav className="navbar navbar-default"> <Navbar>
<div className="container"> <Nav>
<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="#"> <a className="navbar-brand" href="#">
<span>ascribe </span> <span>ascribe </span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span> <span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</a> </a>
</div> </Nav>
<Nav right>
<div id="navbar" className="navbar-collapse collapse"> <DropdownButton eventKey={3} title={this.state.currentUser.username}>
<ul className="nav navbar-nav navbar-left"> <MenuItem eventKey="1" href="/art/account_settings/">{getLangText('Account Settings')}</MenuItem>
</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 className="divider"></li>
<li><a href="/art/faq/">FAQ</a></li> <MenuItem eventKey="2" href="/art/faq/">{getLangText('FAQ')}</MenuItem>
<li><a href="/art/terms/">Terms of Service</a></li> <MenuItem eventKey="3" href="/art/terms/">{getLangText('Terms of Service')}</MenuItem>
<li className="divider"></li> <MenuItem divider />
<li><a href="/api/users/logout/">Log out</a></li> <MenuItem eventKey="4" href="/api/users/logout/">{getLangText('Log out')}</MenuItem>
</ul> </DropdownButton>
</li> </Nav>
</ul> </Navbar>
</div>
</div>
</nav>
); );
} }
}); });

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 fetch from '../utils/fetch';
import AppConstants from '../constants/application_constants'; import AppConstants from '../constants/application_constants';
import FetchApiUtils from '../utils/fetch_api_utils';
let EditionFetcher = { let EditionFetcher = {

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import GeneralUtils from '../utils/general_utils'; import { sumNumList } from '../utils/general_utils';
let TableColumnMixin = { let TableColumnMixin = {
/** /**
@ -11,7 +11,7 @@ let TableColumnMixin = {
let bootstrapClasses = ['col-xs-', 'col-sm-', 'col-md-', 'col-lg-']; let bootstrapClasses = ['col-xs-', 'col-sm-', 'col-md-', 'col-lg-'];
let listOfRowValues = list.map((column) => column.rowWidth ); let listOfRowValues = list.map((column) => column.rowWidth );
let numOfUsedColumns = GeneralUtils.sumNumList(listOfRowValues); let numOfUsedColumns = sumNumList(listOfRowValues);
if(numOfUsedColumns > numOfColumns) { 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.') 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 alt from '../alt';
import EditionAction from '../actions/edition_actions'; import EditionActions from '../actions/edition_actions';
class EditionStore { class EditionStore {
constructor() { constructor() {
this.edition = {}; this.edition = {};
this.bindActions(EditionAction); this.bindActions(EditionActions);
} }
onUpdateEdition(edition) { onUpdateEdition(edition) {

View File

@ -1,5 +1,5 @@
import { default as _fetch } from 'isomorphic-fetch'; 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 {}; class UrlMapError extends Error {};
@ -66,7 +66,7 @@ class Fetch {
}); });
if (attachParamsToQuery && params && Object.keys(params).length > 0) { if (attachParamsToQuery && params && Object.keys(params).length > 0) {
newUrl += FetchApiUtils.argsToQueryParams(params); newUrl += argsToQueryParams(params);
} }
return newUrl; 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 // TODO: Create Unittests that test all functions
let FetchApiUtils = {
/** /**
* Takes a key-value object of this form: * Takes a key-value object of this form:
@ -20,9 +18,9 @@ let FetchApiUtils = {
* CamelCase gets converted to snake_case! * CamelCase gets converted to snake_case!
* *
*/ */
argsToQueryParams(obj) { export function argsToQueryParams(obj) {
obj = GeneralUtils.sanitize(obj); obj = sanitize(obj);
return Object return Object
.keys(obj) .keys(obj)
@ -40,13 +38,13 @@ let FetchApiUtils = {
return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]); return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]);
}) })
.join(''); .join('');
}, };
/** /**
* Takes a string and a boolean and generates a string query parameter for * Takes a string and a boolean and generates a string query parameter for
* an API call. * an API call.
*/ */
generateOrderingQueryParams(orderBy, orderAsc) { export function generateOrderingQueryParams(orderBy, orderAsc) {
let interpolation = ''; let interpolation = '';
if(!orderAsc) { if(!orderAsc) {
@ -54,14 +52,11 @@ let FetchApiUtils = {
} }
return interpolation + orderBy; return interpolation + orderBy;
}, };
status(response) { export function status(response) {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return response return response
} }
throw new Error(response.json()) throw new Error(response.json())
}
}; };
export default FetchApiUtils;

View File

@ -1,10 +1,6 @@
// TODO: Create Unittests that test all functions // TODO: Create Unittests that test all functions
let GeneralUtils = { export function sanitize(obj) {
/**
* Removes undefined and null values from an key-value object.
*/
sanitize(obj) {
Object Object
.keys(obj) .keys(obj)
.map((key) => { .map((key) => {
@ -16,26 +12,52 @@ let GeneralUtils = {
}); });
return obj; return obj;
}, };
/** /**
* Returns the values of an object. * Returns the values of an object.
*/ */
valuesOfObject(obj) { export function valuesOfObject(obj) {
return Object return Object
.keys(obj) .keys(obj)
.map(key => obj[key]); .map(key => obj[key]);
}, };
/** /**
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum... * Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
*/ */
sumNumList(l) { export function sumNumList(l) {
let sum = 0; let sum = 0;
l.forEach((num) => sum += parseFloat(num) || 0); l.forEach((num) => sum += parseFloat(num) || 0);
return sum; return sum;
}
}; };
export default GeneralUtils; /*
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);
}
}
};