Adding custom language setting.

- Fixes #319
This commit is contained in:
Dessalines 2019-12-09 00:24:53 -08:00
parent 4c7da003a2
commit f0808a1116
20 changed files with 107 additions and 17 deletions

18
README.md vendored
View file

@ -247,15 +247,15 @@ If you'd like to add translations, take a look a look at the [English translatio
lang | done | missing lang | done | missing
--- | --- | --- --- | --- | ---
de | 79% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,subscribed,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,expires,recent_comments,nsfw,show_nsfw,theme,crypto,monero,joined,by,to,transfer_community,transfer_site,are_you_sure,yes,no de | 78% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,subscribed,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,expires,language,browser_default,recent_comments,nsfw,show_nsfw,theme,crypto,monero,joined,by,to,transfer_community,transfer_site,are_you_sure,yes,no
eo | 87% | number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,theme,are_you_sure,yes,no eo | 86% | number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,theme,are_you_sure,yes,no
es | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup es | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default
fr | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup fr | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default
it | 97% | archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup it | 96% | archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default
nl | 89% | preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,theme nl | 88% | preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,theme
ru | 83% | cross_posts,cross_post,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no ru | 82% | cross_posts,cross_post,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
sv | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup sv | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default
zh | 81% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no zh | 80% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
If you'd like to update this report, run: If you'd like to update this report, run:

2
install.sh vendored
View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
export DATABASE_URL=postgres://rrr:rrr@localhost/rrr export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
export JWT_SECRET=changeme export JWT_SECRET=changeme
export HOSTNAME=rrr export HOSTNAME=rrr

View file

@ -0,0 +1 @@
alter table user_ drop column lang;

View file

@ -0,0 +1 @@
alter table user_ add column lang varchar(20) default 'browser' not null;

View file

@ -25,6 +25,7 @@ pub struct SaveUserSettings {
theme: String, theme: String,
default_sort_type: i16, default_sort_type: i16,
default_listing_type: i16, default_listing_type: i16,
lang: String,
auth: String, auth: String,
} }
@ -220,6 +221,7 @@ impl Perform<LoginResponse> for Oper<Register> {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
// Create the user // Create the user
@ -313,6 +315,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
theme: data.theme.to_owned(), theme: data.theme.to_owned(),
default_sort_type: data.default_sort_type, default_sort_type: data.default_sort_type,
default_listing_type: data.default_listing_type, default_listing_type: data.default_listing_type,
lang: data.lang.to_owned(),
}; };
let updated_user = match User_::update(&conn, user_id, &user_form) { let updated_user = match User_::update(&conn, user_id, &user_form) {
@ -445,6 +448,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
theme: read_user.theme, theme: read_user.theme,
default_sort_type: read_user.default_sort_type, default_sort_type: read_user.default_sort_type,
default_listing_type: read_user.default_listing_type, default_listing_type: read_user.default_listing_type,
lang: read_user.lang,
}; };
match User_::update(&conn, data.user_id, &user_form) { match User_::update(&conn, data.user_id, &user_form) {
@ -506,6 +510,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
theme: read_user.theme, theme: read_user.theme,
default_sort_type: read_user.default_sort_type, default_sort_type: read_user.default_sort_type,
default_listing_type: read_user.default_listing_type, default_listing_type: read_user.default_listing_type,
lang: read_user.lang,
}; };
match User_::update(&conn, data.user_id, &user_form) { match User_::update(&conn, data.user_id, &user_form) {
@ -842,6 +847,7 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
theme: read_user.theme, theme: read_user.theme,
default_sort_type: read_user.default_sort_type, default_sort_type: read_user.default_sort_type,
default_listing_type: read_user.default_listing_type, default_listing_type: read_user.default_listing_type,
lang: read_user.lang,
}; };
let updated_user = match User_::update_password(&conn, user_id, &user_form) { let updated_user = match User_::update_password(&conn, user_id, &user_form) {

View file

@ -76,6 +76,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let person = expected_user.person(); let person = expected_user.person();

View file

@ -181,6 +181,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -415,6 +415,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -267,6 +267,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -449,6 +449,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_mod = User_::create(&conn, &new_mod).unwrap(); let inserted_mod = User_::create(&conn, &new_mod).unwrap();
@ -466,6 +467,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -92,6 +92,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -194,6 +194,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -345,6 +345,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -23,6 +23,7 @@ pub struct User_ {
pub theme: String, pub theme: String,
pub default_sort_type: i16, pub default_sort_type: i16,
pub default_listing_type: i16, pub default_listing_type: i16,
pub lang: String,
} }
#[derive(Insertable, AsChangeset, Clone)] #[derive(Insertable, AsChangeset, Clone)]
@ -40,6 +41,7 @@ pub struct UserForm {
pub theme: String, pub theme: String,
pub default_sort_type: i16, pub default_sort_type: i16,
pub default_listing_type: i16, pub default_listing_type: i16,
pub lang: String,
} }
impl Crud<UserForm> for User_ { impl Crud<UserForm> for User_ {
@ -96,6 +98,7 @@ pub struct Claims {
pub theme: String, pub theme: String,
pub default_sort_type: i16, pub default_sort_type: i16,
pub default_listing_type: i16, pub default_listing_type: i16,
pub lang: String,
} }
impl Claims { impl Claims {
@ -119,6 +122,7 @@ impl User_ {
theme: self.theme.to_owned(), theme: self.theme.to_owned(),
default_sort_type: self.default_sort_type, default_sort_type: self.default_sort_type,
default_listing_type: self.default_listing_type, default_listing_type: self.default_listing_type,
lang: self.lang.to_owned(),
}; };
encode( encode(
&Header::default(), &Header::default(),
@ -175,6 +179,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();
@ -195,6 +200,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let read_user = User_::read(&conn, inserted_user.id).unwrap(); let read_user = User_::read(&conn, inserted_user.id).unwrap();

View file

@ -75,6 +75,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_user = User_::create(&conn, &new_user).unwrap(); let inserted_user = User_::create(&conn, &new_user).unwrap();
@ -92,6 +93,7 @@ mod tests {
theme: "darkly".into(), theme: "darkly".into(),
default_sort_type: SortType::Hot as i16, default_sort_type: SortType::Hot as i16,
default_listing_type: ListingType::Subscribed as i16, default_listing_type: ListingType::Subscribed as i16,
lang: "browser".into(),
}; };
let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();

View file

@ -266,6 +266,7 @@ table! {
theme -> Varchar, theme -> Varchar,
default_sort_type -> Int2, default_sort_type -> Int2,
default_listing_type -> Int2, default_listing_type -> Int2,
lang -> Varchar,
} }
} }

View file

@ -27,6 +27,7 @@ import {
capitalizeFirstLetter, capitalizeFirstLetter,
themes, themes,
setTheme, setTheme,
languages,
} from '../utils'; } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
@ -94,6 +95,7 @@ export class User extends Component<any, UserState> {
theme: null, theme: null,
default_sort_type: null, default_sort_type: null,
default_listing_type: null, default_listing_type: null,
lang: null,
auth: null, auth: null,
}, },
userSettingsLoading: null, userSettingsLoading: null,
@ -420,6 +422,32 @@ export class User extends Component<any, UserState> {
<T i18nKey="settings">#</T> <T i18nKey="settings">#</T>
</h5> </h5>
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}> <form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
<div class="form-group">
<div class="col-12">
<label>
<T i18nKey="language">#</T>
</label>
<select
value={this.state.userSettingsForm.lang}
onChange={linkEvent(
this,
this.handleUserSettingsLangChange
)}
class="ml-2 custom-select custom-select-sm w-auto"
>
<option disabled>
<T i18nKey="language">#</T>
</option>
<option value="browser">
<T i18nKey="browser_default">#</T>
</option>
<option disabled></option>
{languages.map(lang => (
<option value={lang.code}>{lang.name}</option>
))}
</select>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-12"> <div class="col-12">
<label> <label>
@ -693,6 +721,12 @@ export class User extends Component<any, UserState> {
i.setState(i.state); i.setState(i.state);
} }
handleUserSettingsLangChange(i: User, event: any) {
i.state.userSettingsForm.lang = event.target.value;
i18n.changeLanguage(i.state.userSettingsForm.lang);
i.setState(i.state);
}
handleUserSettingsSortTypeChange(val: SortType) { handleUserSettingsSortTypeChange(val: SortType) {
this.state.userSettingsForm.default_sort_type = val; this.state.userSettingsForm.default_sort_type = val;
this.setState(this.state); this.setState(this.state);
@ -762,6 +796,7 @@ export class User extends Component<any, UserState> {
UserService.Instance.user.default_sort_type; UserService.Instance.user.default_sort_type;
this.state.userSettingsForm.default_listing_type = this.state.userSettingsForm.default_listing_type =
UserService.Instance.user.default_listing_type; UserService.Instance.user.default_listing_type;
this.state.userSettingsForm.lang = UserService.Instance.user.lang;
} }
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`; document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
window.scrollTo(0, 0); window.scrollTo(0, 0);

View file

@ -79,6 +79,7 @@ export interface User {
theme: string; theme: string;
default_sort_type: SortType; default_sort_type: SortType;
default_listing_type: ListingType; default_listing_type: ListingType;
lang: string;
} }
export interface UserView { export interface UserView {
@ -469,6 +470,7 @@ export interface UserSettingsForm {
theme: string; theme: string;
default_sort_type: SortType; default_sort_type: SortType;
default_listing_type: ListingType; default_listing_type: ListingType;
lang: string;
auth: string; auth: string;
} }

View file

@ -125,6 +125,8 @@ export const en = {
email: 'Email', email: 'Email',
optional: 'Optional', optional: 'Optional',
expires: 'Expires', expires: 'Expires',
language: 'Language',
browser_default: 'Browser Default',
url: 'URL', url: 'URL',
body: 'Body', body: 'Body',
copy_suggested_title: 'copy suggested title: {{title}}', copy_suggested_title: 'copy suggested title: {{title}}',

39
ui/src/utils.ts vendored
View file

@ -16,6 +16,7 @@ import {
ListingType, ListingType,
SearchType, SearchType,
} from './interfaces'; } from './interfaces';
import { UserService } from './services/UserService';
import * as markdown_it from 'markdown-it'; import * as markdown_it from 'markdown-it';
import * as markdownitEmoji from 'markdown-it-emoji/light'; import * as markdownitEmoji from 'markdown-it-emoji/light';
import * as markdown_it_container from 'markdown-it-container'; import * as markdown_it_container from 'markdown-it-container';
@ -240,16 +241,32 @@ export function debounce(
}; };
} }
export const languages = [
{ code: 'en', name: 'English' },
{ code: 'eo', name: 'Esperanto' },
{ code: 'es', name: 'Español' },
{ code: 'de', name: 'Deutsch' },
{ code: 'zh', name: '中文' },
{ code: 'fr', name: 'Français' },
{ code: 'sv', name: 'Svenska' },
{ code: 'ru', name: 'Русский' },
{ code: 'nl', name: 'Nederlands' },
{ code: 'it', name: 'Italiano' },
];
export function getLanguage(): string { export function getLanguage(): string {
return navigator.language || navigator.userLanguage; let user = UserService.Instance.user;
let lang = user && user.lang ? user.lang : 'browser';
if (lang == 'browser') {
return getBrowserLanguage();
} else {
return lang;
}
} }
export function objectFlip(obj: any) { export function getBrowserLanguage(): string {
const ret = {}; return navigator.language || navigator.userLanguage;
Object.keys(obj).forEach(key => {
ret[obj[key]] = key;
});
return ret;
} }
export function getMomentLanguage(): string { export function getMomentLanguage(): string {
@ -313,3 +330,11 @@ export function setTheme(theme: string = 'darkly') {
} }
document.getElementById(theme).removeAttribute('disabled'); document.getElementById(theme).removeAttribute('disabled');
} }
export function objectFlip(obj: any) {
const ret = {};
Object.keys(obj).forEach(key => {
ret[obj[key]] = key;
});
return ret;
}