]> Untitled Git - lemmy.git/commitdiff
Adding custom language setting.
authorDessalines <tyhou13@gmx.com>
Mon, 9 Dec 2019 08:24:53 +0000 (00:24 -0800)
committerDessalines <tyhou13@gmx.com>
Mon, 9 Dec 2019 08:24:53 +0000 (00:24 -0800)
- Fixes #319

20 files changed:
README.md
install.sh
server/migrations/2019-12-09-060754_add_lang/down.sql [new file with mode: 0644]
server/migrations/2019-12-09-060754_add_lang/up.sql [new file with mode: 0644]
server/src/api/user.rs
server/src/apub.rs
server/src/db/comment.rs
server/src/db/comment_view.rs
server/src/db/community.rs
server/src/db/moderator.rs
server/src/db/password_reset_request.rs
server/src/db/post.rs
server/src/db/post_view.rs
server/src/db/user.rs
server/src/db/user_mention.rs
server/src/schema.rs
ui/src/components/user.tsx
ui/src/interfaces.ts
ui/src/translations/en.ts
ui/src/utils.ts

index 1f06b083ecb237fcd6abcd017a32d59c36702a50..273a1b9848a7b890322bd30e914fc77ca89c7fa4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -247,15 +247,15 @@ If you'd like to add translations, take a look a look at the [English translatio
 
 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 
-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 
-es | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup 
-fr | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup 
-it | 97% | archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup 
-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 
-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 
-sv | 96% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup 
-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 
+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 | 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 | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default 
+fr | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default 
+it | 96% | archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default 
+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 | 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 | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default 
+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:
index 80d3277a10d58c072ae035ffbe3e6a119c04b4cb..bbed1c9b09fd5150444bd45fa3afc9487cc97c26 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 set -e
 
-export DATABASE_URL=postgres://rrr:rrr@localhost/rrr
+export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
 export JWT_SECRET=changeme
 export HOSTNAME=rrr
 
diff --git a/server/migrations/2019-12-09-060754_add_lang/down.sql b/server/migrations/2019-12-09-060754_add_lang/down.sql
new file mode 100644 (file)
index 0000000..c133559
--- /dev/null
@@ -0,0 +1 @@
+alter table user_ drop column lang;
diff --git a/server/migrations/2019-12-09-060754_add_lang/up.sql b/server/migrations/2019-12-09-060754_add_lang/up.sql
new file mode 100644 (file)
index 0000000..98a72ad
--- /dev/null
@@ -0,0 +1 @@
+alter table user_ add column lang varchar(20) default 'browser' not null;
index 7f7af813cfcc80ef9c8a4ac2e5081d4e8098df1a..bc51c8e9bd60991ef7c459ff76875db54ff778f9 100644 (file)
@@ -25,6 +25,7 @@ pub struct SaveUserSettings {
   theme: String,
   default_sort_type: i16,
   default_listing_type: i16,
+  lang: String,
   auth: String,
 }
 
@@ -220,6 +221,7 @@ impl Perform<LoginResponse> for Oper<Register> {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     // Create the user
@@ -313,6 +315,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
       theme: data.theme.to_owned(),
       default_sort_type: data.default_sort_type,
       default_listing_type: data.default_listing_type,
+      lang: data.lang.to_owned(),
     };
 
     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,
       default_sort_type: read_user.default_sort_type,
       default_listing_type: read_user.default_listing_type,
+      lang: read_user.lang,
     };
 
     match User_::update(&conn, data.user_id, &user_form) {
@@ -506,6 +510,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
       theme: read_user.theme,
       default_sort_type: read_user.default_sort_type,
       default_listing_type: read_user.default_listing_type,
+      lang: read_user.lang,
     };
 
     match User_::update(&conn, data.user_id, &user_form) {
@@ -842,6 +847,7 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
       theme: read_user.theme,
       default_sort_type: read_user.default_sort_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) {
index 3c7202b2756dd3c404436e21529ae428e7e2517e..66878d3736621278b5bf7e73f6e77d519bcccc4c 100644 (file)
@@ -76,6 +76,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let person = expected_user.person();
index 64532b8388aa5b2959a9cb976fc1891412e9ada4..b7bd562d2e49485a70bb024475e0e8de40572279 100644 (file)
@@ -181,6 +181,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index dd53ddcacc88d114808831b13c9313b649ed06a6..54c43b93c8493477ec1b7eddb53c6f14157b4b1a 100644 (file)
@@ -415,6 +415,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index bfc6089bc8395b65d15bdf1ea118f8f258d8662e..9546907515612119d7e1365e85bb361ebf768fc1 100644 (file)
@@ -267,6 +267,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index b04c6c623cf3034c2b158a094995ffca5e8171e3..22547ca47164d1b9925e4191b1bdae3c6ee2a24a 100644 (file)
@@ -449,6 +449,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_mod = User_::create(&conn, &new_mod).unwrap();
@@ -466,6 +467,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index 265945df24479251c63540c2e49bd7be06d96c77..8c65b4c0563b49db68cbd867547bd004001b6548 100644 (file)
@@ -92,6 +92,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index f185bd75411fbecfe2dd75064b3f100776f107d0..96ae31db09756b42d91528fd5be07cde457627bb 100644 (file)
@@ -194,6 +194,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index 7a589738766f8cf389b653488cb07bfb37059f7d..4fcd8e47e939d4c9e331ae054aba90a13bd6680f 100644 (file)
@@ -345,6 +345,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
index 064bff39f47276a54bd7cf614b8dc2c262d63bf4..3d3865e8d7039b85f94fca3a303d3abfb0fdd071 100644 (file)
@@ -23,6 +23,7 @@ pub struct User_ {
   pub theme: String,
   pub default_sort_type: i16,
   pub default_listing_type: i16,
+  pub lang: String,
 }
 
 #[derive(Insertable, AsChangeset, Clone)]
@@ -40,6 +41,7 @@ pub struct UserForm {
   pub theme: String,
   pub default_sort_type: i16,
   pub default_listing_type: i16,
+  pub lang: String,
 }
 
 impl Crud<UserForm> for User_ {
@@ -96,6 +98,7 @@ pub struct Claims {
   pub theme: String,
   pub default_sort_type: i16,
   pub default_listing_type: i16,
+  pub lang: String,
 }
 
 impl Claims {
@@ -119,6 +122,7 @@ impl User_ {
       theme: self.theme.to_owned(),
       default_sort_type: self.default_sort_type,
       default_listing_type: self.default_listing_type,
+      lang: self.lang.to_owned(),
     };
     encode(
       &Header::default(),
@@ -175,6 +179,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
@@ -195,6 +200,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let read_user = User_::read(&conn, inserted_user.id).unwrap();
index 668d28fcc3203840226a09033f258730d2b35167..7eb4d486a3f6ee0879f32e23f7532819763ccfcf 100644 (file)
@@ -75,6 +75,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_user = User_::create(&conn, &new_user).unwrap();
@@ -92,6 +93,7 @@ mod tests {
       theme: "darkly".into(),
       default_sort_type: SortType::Hot as i16,
       default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
     };
 
     let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();
index 5fc70953687263e7acf5de4f42585747fbf57392..bd73aabf5cf000e6c8385623062f46b95c004552 100644 (file)
@@ -266,6 +266,7 @@ table! {
         theme -> Varchar,
         default_sort_type -> Int2,
         default_listing_type -> Int2,
+        lang -> Varchar,
     }
 }
 
index 0aba995fb4b2315d890a2e60646e087073da543b..bf77d4b7df0851e1dc8a229f05e99831dbd0580e 100644 (file)
@@ -27,6 +27,7 @@ import {
   capitalizeFirstLetter,
   themes,
   setTheme,
+  languages,
 } from '../utils';
 import { PostListing } from './post-listing';
 import { SortSelect } from './sort-select';
@@ -94,6 +95,7 @@ export class User extends Component<any, UserState> {
       theme: null,
       default_sort_type: null,
       default_listing_type: null,
+      lang: null,
       auth: null,
     },
     userSettingsLoading: null,
@@ -420,6 +422,32 @@ export class User extends Component<any, UserState> {
               <T i18nKey="settings">#</T>
             </h5>
             <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="col-12">
                   <label>
@@ -693,6 +721,12 @@ export class User extends Component<any, UserState> {
     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) {
     this.state.userSettingsForm.default_sort_type = val;
     this.setState(this.state);
@@ -762,6 +796,7 @@ export class User extends Component<any, UserState> {
           UserService.Instance.user.default_sort_type;
         this.state.userSettingsForm.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}`;
       window.scrollTo(0, 0);
index 9cd9bef46c64c638f5f0cc4cf071473f1a797230..f327202a494a50910bf55e8893bd885a5f6c27fc 100644 (file)
@@ -79,6 +79,7 @@ export interface User {
   theme: string;
   default_sort_type: SortType;
   default_listing_type: ListingType;
+  lang: string;
 }
 
 export interface UserView {
@@ -469,6 +470,7 @@ export interface UserSettingsForm {
   theme: string;
   default_sort_type: SortType;
   default_listing_type: ListingType;
+  lang: string;
   auth: string;
 }
 
index e20ba40a580340b79759793aca75bfb744a1bdc7..ce5fcdcc07be815c2f63102dd996f9f12e023376 100644 (file)
@@ -125,6 +125,8 @@ export const en = {
     email: 'Email',
     optional: 'Optional',
     expires: 'Expires',
+    language: 'Language',
+    browser_default: 'Browser Default',
     url: 'URL',
     body: 'Body',
     copy_suggested_title: 'copy suggested title: {{title}}',
index 2cf80d1411553422446b371758876ecb1a489419..1bbda3aa3d35bb879386a11f4546ba8c9488dafb 100644 (file)
@@ -16,6 +16,7 @@ import {
   ListingType,
   SearchType,
 } from './interfaces';
+import { UserService } from './services/UserService';
 import * as markdown_it from 'markdown-it';
 import * as markdownitEmoji from 'markdown-it-emoji/light';
 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 {
-  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) {
-  const ret = {};
-  Object.keys(obj).forEach(key => {
-    ret[obj[key]] = key;
-  });
-  return ret;
+export function getBrowserLanguage(): string {
+  return navigator.language || navigator.userLanguage;
 }
 
 export function getMomentLanguage(): string {
@@ -313,3 +330,11 @@ export function setTheme(theme: string = 'darkly') {
   }
   document.getElementById(theme).removeAttribute('disabled');
 }
+
+export function objectFlip(obj: any) {
+  const ret = {};
+  Object.keys(obj).forEach(key => {
+    ret[obj[key]] = key;
+  });
+  return ret;
+}