mirror of
				https://git.sr.ht/~cadence/bibliogram
				synced 2025-10-31 19:45:37 +00:00 
			
		
		
		
	Create initial language support
Create support for languages, then reformat user, home, and post pages to use it, and create en and en-us language files.
This commit is contained in:
		
							parent
							
								
									1f76e43446
								
							
						
					
					
						commit
						496d53f47e
					
				| @ -7,6 +7,7 @@ | |||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "cd src/site && node server.js", |     "start": "cd src/site && node server.js", | ||||||
|     "assistant": "cd src/site && node assistant.js", |     "assistant": "cd src/site && node assistant.js", | ||||||
|  |     "build-lang": "cd src/lang/utils && node build_base.js", | ||||||
|     "test": "tap" |     "test": "tap" | ||||||
|   }, |   }, | ||||||
|   "keywords": [], |   "keywords": [], | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								src/lang/base.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/lang/base.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | // This file was automatically generated and its contents will be overwritten later.
 | ||||||
|  | 
 | ||||||
|  | const data = { | ||||||
|  | 	"go_to_profile": "MISSING STRING: go_to_profile", | ||||||
|  | 	"go_to_post": "MISSING STRING: go_to_post", | ||||||
|  | 	"go_username_or_url": "MISSING STRING: go_username_or_url", | ||||||
|  | 	"go_shortcode_or_url": "MISSING STRING: go_shortcode_or_url", | ||||||
|  | 	"go_button": "MISSING STRING: go_button", | ||||||
|  | 	"about_bibliogram_header": "MISSING STRING: about_bibliogram_header", | ||||||
|  | 	"pug_about_bibliogram_content": locals => "MISSING TEMPLATE: pug_about_bibliogram_content", | ||||||
|  | 	"about_this_instance_header": "MISSING STRING: about_this_instance_header", | ||||||
|  | 	"onion_site_available": "MISSING STRING: onion_site_available", | ||||||
|  | 	"t_settings": "MISSING STRING: t_settings", | ||||||
|  | 	"t_privacy_policy": "MISSING STRING: t_privacy_policy", | ||||||
|  | 	"has_not_written_privacy_policy": "MISSING STRING: has_not_written_privacy_policy", | ||||||
|  | 	"instance_not_blocked": "MISSING STRING: instance_not_blocked", | ||||||
|  | 	"instance_partially_blocked": "MISSING STRING: instance_partially_blocked", | ||||||
|  | 	"instance_blocked": "MISSING STRING: instance_blocked", | ||||||
|  | 	"rss_enabled": "MISSING STRING: rss_enabled", | ||||||
|  | 	"rss_disabled": "MISSING STRING: rss_disabled", | ||||||
|  | 	"external_links_header": "MISSING STRING: external_links_header", | ||||||
|  | 	"source_link": "MISSING STRING: source_link", | ||||||
|  | 	"matrix_link": "MISSING STRING: matrix_link", | ||||||
|  | 	"instances_link": "MISSING STRING: instances_link", | ||||||
|  | 	"contact_link": "MISSING STRING: contact_link", | ||||||
|  | 	"t_featured_profiles": "MISSING STRING: t_featured_profiles", | ||||||
|  | 	"featured_profiles_whats_this": "MISSING STRING: featured_profiles_whats_this", | ||||||
|  | 	"html_featured_profiles_disclaimer": "MISSING STRING: html_featured_profiles_disclaimer", | ||||||
|  | 	"verified_badge_alt": "MISSING STRING: verified_badge_alt", | ||||||
|  | 	"verified_badge_title": "MISSING STRING: verified_badge_title", | ||||||
|  | 	"post_counter_label": "MISSING STRING: post_counter_label", | ||||||
|  | 	"outgoing_follows_counter_label": "MISSING STRING: outgoing_follows_counter_label", | ||||||
|  | 	"incoming_follows_counter_label": "MISSING STRING: incoming_follows_counter_label", | ||||||
|  | 	"t_home": "MISSING STRING: t_home", | ||||||
|  | 	"tab_timeline": "MISSING STRING: tab_timeline", | ||||||
|  | 	"tab_igtv": "MISSING STRING: tab_igtv", | ||||||
|  | 	"next_page_button": "MISSING STRING: next_page_button", | ||||||
|  | 	"next_page_button_loading": "MISSING STRING: next_page_button_loading", | ||||||
|  | 	"profile_is_private_notice": "MISSING STRING: profile_is_private_notice", | ||||||
|  | 	"no_posts_notice": "MISSING STRING: no_posts_notice", | ||||||
|  | 	"no_more_posts_notice": "MISSING STRING: no_more_posts_notice", | ||||||
|  | 	"fn_page_divider": () => "MISSING FUNCTION: fn_page_divider", | ||||||
|  | 	"pug_post_timestamp": locals => "MISSING TEMPLATE: pug_post_timestamp" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = data | ||||||
							
								
								
									
										3
									
								
								src/lang/en-us.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/lang/en-us.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | const data = {...require("./en")} | ||||||
|  | 
 | ||||||
|  | module.exports = data | ||||||
							
								
								
									
										71
									
								
								src/lang/en.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/lang/en.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | const compile = require("pug").compile | ||||||
|  | const data = {...require("./base")} | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @param {string} text | ||||||
|  |  */ | ||||||
|  | function pug(text) { | ||||||
|  | 	let lines = text.split("\n") | ||||||
|  | 	while (lines[0] === "") lines.shift() | ||||||
|  | 	const indentLevel = lines[0].match(/^\t*/)[0].length | ||||||
|  | 	lines = lines.map(l => l.replace(new RegExp(`^\\t{0,${indentLevel}}`), "")) | ||||||
|  | 	return compile(lines.join("\n")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ;(() => { | ||||||
|  | 	data.go_to_profile = "Go to profile" | ||||||
|  | 	data.go_to_post = "Go to post" | ||||||
|  | 	data.go_username_or_url = "Username or URL" | ||||||
|  | 	data.go_shortcode_or_url = "Shortcode or URL" | ||||||
|  | 	data.go_button = "Go" | ||||||
|  | 	data.about_bibliogram_header = "About Bibliogram" | ||||||
|  | 	data.pug_about_bibliogram_content = pug(` | ||||||
|  | 		p. | ||||||
|  | 			Bibliogram is a website that takes data from Instagram's public profile views and puts it into | ||||||
|  | 			a friendlier page that loads faster, gives downloadable images, eliminates ads, | ||||||
|  | 			generates RSS feeds, and doesn't urge you to sign up. #[a(href=(link_to_featured_profiles ? "#featured-profiles" : "/u/instagram")).example-link See an example.] | ||||||
|  | 		p. | ||||||
|  | 			Bibliogram does #[em not] allow you to anonymously post, like, comment, follow, or view private profiles. | ||||||
|  | 			It does not preserve deleted posts. | ||||||
|  | 	`)
 | ||||||
|  | 	data.about_this_instance_header = "About this instance" | ||||||
|  | 	data.onion_site_available = "Onion site available" | ||||||
|  | 	data.t_settings = "Settings" | ||||||
|  | 	data.t_privacy_policy = "Privacy policy" | ||||||
|  | 	data.has_not_written_privacy_policy = "Owner has not written a privacy policy" | ||||||
|  | 	data.instance_not_blocked = "Instance is not blocked" | ||||||
|  | 	data.instance_partially_blocked = "Instance is partially blocked" | ||||||
|  | 	data.instance_blocked = "Instance is blocked" | ||||||
|  | 	data.rss_disabled = "RSS feeds are disabled" | ||||||
|  | 	data.rss_enabled = "RSS feeds are enabled" | ||||||
|  | 	data.external_links_header = "External links" | ||||||
|  | 	data.source_link = "Code on sourcehut" | ||||||
|  | 	data.matrix_link = "Discussion room on Matrix" | ||||||
|  | 	data.instances_link = "Other Bibliogram instances" | ||||||
|  | 	data.contact_link = "Contact the developer" | ||||||
|  | 	data.t_featured_profiles = "Featured profiles" | ||||||
|  | 	data.featured_profiles_whats_this = "What's this?" | ||||||
|  | 	data.html_featured_profiles_disclaimer = pug(` | ||||||
|  | 		p The owner of this website personally thinks that these profiles are interesting. | ||||||
|  | 		p These are not endorsements from the Bibliogram project. | ||||||
|  | 	`)()
 | ||||||
|  | 	data.verified_badge_title = "Verified" | ||||||
|  | 	data.verified_badge_alt = "Verified." | ||||||
|  | 	data.post_counter_label = "posts" | ||||||
|  | 	data.outgoing_follows_counter_label = "following" | ||||||
|  | 	data.incoming_follows_counter_label = "followed by" | ||||||
|  | 	data.t_home = "Home" | ||||||
|  | 	data.tab_timeline = "Timeline" | ||||||
|  | 	data.tab_igtv = "IGTV" | ||||||
|  | 	data.next_page_button = "Next page" | ||||||
|  | 	data.next_page_button_loading = "Loading..." | ||||||
|  | 	data.profile_is_private_notice = "Profile is private." | ||||||
|  | 	data.no_posts_notice = "No posts." | ||||||
|  | 	data.no_more_posts_notice = "No more posts." | ||||||
|  | 	data.fn_page_divider = number => `Page ${number}` | ||||||
|  | 	data.pug_post_timestamp = pug(` | ||||||
|  | 		| Posted on #[time(datetime=post.date.toISOString() data-local-date)= post.getDisplayDate()]. | ||||||
|  | 	`)
 | ||||||
|  | })() | ||||||
|  | 
 | ||||||
|  | module.exports = data | ||||||
							
								
								
									
										39
									
								
								src/lang/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/lang/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | const base = require("./base") | ||||||
|  | 
 | ||||||
|  | class Lang { | ||||||
|  | 	constructor() { | ||||||
|  | 		/** @type {Map<string, import("./base")>} */ | ||||||
|  | 		this.backing = new Map() | ||||||
|  | 
 | ||||||
|  | 		this.backing.set("base", require("./base")) | ||||||
|  | 
 | ||||||
|  | 		for (const code of ["en", "en-us"]) { | ||||||
|  | 			// Assign lang
 | ||||||
|  | 			const data = require(`./${code}`) | ||||||
|  | 			this.backing.set(code, data) | ||||||
|  | 			// Check properties
 | ||||||
|  | 			for (const key of Object.keys(base)) { | ||||||
|  | 				if (!data[key] || data[key] === base[key]) { | ||||||
|  | 					console.log(`[!] [${code}] ${key} was not replaced`) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {string} code | ||||||
|  | 	 */ | ||||||
|  | 	get(code) { | ||||||
|  | 		if (this.backing.has(code)) { | ||||||
|  | 			// console.log(`[.] Getting language code ${code}`)
 | ||||||
|  | 			return this.backing.get(code) | ||||||
|  | 		} else { | ||||||
|  | 			console.log(`[!] WARNING: tried to get missing language code ${code}`) | ||||||
|  | 			return this.backing.get("base") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const lang = new Lang() | ||||||
|  | 
 | ||||||
|  | module.exports = lang | ||||||
							
								
								
									
										7
									
								
								src/lang/utils/base.template.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/lang/utils/base.template.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | // This file is a template.
 | ||||||
|  | 
 | ||||||
|  | const data = { | ||||||
|  | 	// CONTENT
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = data | ||||||
							
								
								
									
										51
									
								
								src/lang/utils/base.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/lang/utils/base.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | # Front page | ||||||
|  | 
 | ||||||
|  | # Top section | ||||||
|  | go_to_profile | ||||||
|  | go_to_post | ||||||
|  | go_username_or_url | ||||||
|  | go_shortcode_or_url | ||||||
|  | go_button | ||||||
|  | 
 | ||||||
|  | # Bottom section | ||||||
|  | about_bibliogram_header | ||||||
|  | pug_about_bibliogram_content | ||||||
|  | about_this_instance_header | ||||||
|  | onion_site_available | ||||||
|  | t_settings | ||||||
|  | t_privacy_policy | ||||||
|  | has_not_written_privacy_policy | ||||||
|  | instance_not_blocked | ||||||
|  | instance_partially_blocked | ||||||
|  | instance_blocked | ||||||
|  | rss_enabled | ||||||
|  | rss_disabled | ||||||
|  | external_links_header | ||||||
|  | source_link | ||||||
|  | matrix_link | ||||||
|  | instances_link | ||||||
|  | contact_link | ||||||
|  | t_featured_profiles | ||||||
|  | featured_profiles_whats_this | ||||||
|  | html_featured_profiles_disclaimer | ||||||
|  | 
 | ||||||
|  | # User page | ||||||
|  | 
 | ||||||
|  | verified_badge_alt | ||||||
|  | verified_badge_title | ||||||
|  | post_counter_label | ||||||
|  | outgoing_follows_counter_label | ||||||
|  | incoming_follows_counter_label | ||||||
|  | t_home | ||||||
|  | tab_timeline | ||||||
|  | tab_igtv | ||||||
|  | next_page_button | ||||||
|  | next_page_button_loading | ||||||
|  | profile_is_private_notice | ||||||
|  | no_posts_notice | ||||||
|  | no_more_posts_notice | ||||||
|  | fn_page_divider | ||||||
|  | 
 | ||||||
|  | # Post page | ||||||
|  | 
 | ||||||
|  | pug_post_timestamp | ||||||
							
								
								
									
										28
									
								
								src/lang/utils/build_base.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/lang/utils/build_base.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | const fs = require("fs").promises | ||||||
|  | const pj = require("path").join | ||||||
|  | 
 | ||||||
|  | ;(async () => { | ||||||
|  | 	const contents = await fs.readFile(pj(__dirname, "base.txt"), "utf8") | ||||||
|  | 	const lines = contents.split("\n") | ||||||
|  | 	let template = await fs.readFile(pj(__dirname, "base.template.js"), "utf8") | ||||||
|  | 
 | ||||||
|  | 	template = template | ||||||
|  | 		.replace("// This file is a template.", "// This file was automatically generated and its contents will be overwritten later.") | ||||||
|  | 		.replace("// CONTENT", lines | ||||||
|  | 			.filter(l => l && !l.startsWith("#")) | ||||||
|  | 			.map(l => { | ||||||
|  | 				if (l.startsWith("pug_")) { | ||||||
|  | 					return `"${l}": locals => "MISSING TEMPLATE: ${l}"` | ||||||
|  | 				} else if (l.startsWith("fn_")) { | ||||||
|  | 					return `"${l}": () => "MISSING FUNCTION: ${l}"` | ||||||
|  | 				} else { | ||||||
|  | 					return `"${l}": "MISSING STRING: ${l}"` | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 			.join(",\n\t") | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 	await fs.writeFile(pj(__dirname, "../base.js"), template, "utf8") | ||||||
|  | 
 | ||||||
|  | 	console.log("base.js written.") | ||||||
|  | })() | ||||||
| @ -107,6 +107,7 @@ let constants = { | |||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	default_user_settings: { | 	default_user_settings: { | ||||||
|  | 		language: "en", | ||||||
| 		rewrite_youtube: "invidio.us", | 		rewrite_youtube: "invidio.us", | ||||||
| 		rewrite_twitter: "nitter.net" | 		rewrite_twitter: "nitter.net" | ||||||
| 	}, | 	}, | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| const constants = require("../../lib/constants") | const constants = require("../../lib/constants") | ||||||
|  | const lang = require("../../lang") | ||||||
| const switcher = require("../../lib/utils/torswitcher") | const switcher = require("../../lib/utils/torswitcher") | ||||||
| const {fetchUser, getOrFetchShortcode, userRequestCache, history, assistantSwitcher} = require("../../lib/collectors") | const {fetchUser, getOrFetchShortcode, userRequestCache, history, assistantSwitcher} = require("../../lib/collectors") | ||||||
| const {render, redirect, getStaticURL} = require("pinski/plugins") | const {render, redirect, getStaticURL} = require("pinski/plugins") | ||||||
| @ -112,7 +113,8 @@ module.exports = [ | |||||||
| 							username, | 							username, | ||||||
| 							expiresMinutes: userRequestCache.getTtl("user/"+username, 1000*60), | 							expiresMinutes: userRequestCache.getTtl("user/"+username, 1000*60), | ||||||
| 							getStaticURL, | 							getStaticURL, | ||||||
| 							settings | 							settings, | ||||||
|  | 							lang | ||||||
| 						}) | 						}) | ||||||
| 					} | 					} | ||||||
| 				} else if (error === constants.symbols.extractor_results.AGE_RESTRICTED) { | 				} else if (error === constants.symbols.extractor_results.AGE_RESTRICTED) { | ||||||
| @ -179,7 +181,7 @@ module.exports = [ | |||||||
| 					contentType: "application/json", | 					contentType: "application/json", | ||||||
| 					content: { | 					content: { | ||||||
| 						title: getPageTitle(post), | 						title: getPageTitle(post), | ||||||
| 						html: pugCache.get("pug/fragments/post.pug").web({post, settings, getStaticURL}) | 						html: pugCache.get("pug/fragments/post.pug").web({lang, post, settings, getStaticURL}) | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			}).catch(error => { | 			}).catch(error => { | ||||||
|  | |||||||
| @ -57,7 +57,7 @@ class ElemJS { | |||||||
| 	event(name, callback) { | 	event(name, callback) { | ||||||
| 		this.element.addEventListener(name, event => callback(event)) | 		this.element.addEventListener(name, event => callback(event)) | ||||||
| 	} | 	} | ||||||
| 	child(toAdd, position) { | 	child(toAdd, position = undefined) { | ||||||
| 		if (typeof(toAdd) == "object") { | 		if (typeof(toAdd) == "object") { | ||||||
| 			toAdd.parent = this; | 			toAdd.parent = this; | ||||||
| 			if (typeof(position) == "number" && position >= 0) { | 			if (typeof(position) == "number" && position >= 0) { | ||||||
|  | |||||||
| @ -75,7 +75,7 @@ class NextPage extends FreezeWidth { | |||||||
| 	fetch() { | 	fetch() { | ||||||
| 		if (this.fetching) return | 		if (this.fetching) return | ||||||
| 		this.fetching = true | 		this.fetching = true | ||||||
| 		this.freeze("Loading...") | 		this.freeze(this.element.getAttribute("data-loading-text")) | ||||||
| 		const type = this.element.getAttribute("data-type") | 		const type = this.element.getAttribute("data-type") | ||||||
| 
 | 
 | ||||||
| 		return fetch(`/fragment/user/${this.element.getAttribute("data-username")}/${this.nextPageNumber}?type=${type}`).then(res => res.text()).then(text => { | 		return fetch(`/fragment/user/${this.element.getAttribute("data-username")}/${this.nextPageNumber}?type=${type}`).then(res => res.text()).then(text => { | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| //- Needs post | //- Needs post | ||||||
| 
 | 
 | ||||||
|  | - const ll = lang.get(settings.language) | ||||||
|  | 
 | ||||||
| include ../includes/post.pug | include ../includes/post.pug | ||||||
| 
 | 
 | ||||||
| +post(post, true) | +post(post, true) | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| //- Needs user, selectedTimeline, url, type | //- Needs user, selectedTimeline, url, type | ||||||
| 
 | 
 | ||||||
|  | - const ll = lang.get(settings.language) | ||||||
|  | 
 | ||||||
| include ../includes/timeline_page.pug | include ../includes/timeline_page.pug | ||||||
| include ../includes/next_page_button.pug | include ../includes/next_page_button.pug | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| //- Needs rssEnabled, allUnblocked, torAvailable, hasPrivacyPolicy, onionLocation | //- Needs rssEnabled, allUnblocked, torAvailable, hasPrivacyPolicy, onionLocation | ||||||
| 
 | 
 | ||||||
|  | - const ll = lang.get(settings.language) | ||||||
|  | 
 | ||||||
| doctype html | doctype html | ||||||
| html | html | ||||||
| 	head | 	head | ||||||
| @ -13,57 +15,53 @@ html | |||||||
| 		.go-sections-container | 		.go-sections-container | ||||||
| 			.go-sections | 			.go-sections | ||||||
| 				section | 				section | ||||||
| 					h2.title Go to profile | 					h2.title= ll.go_to_profile | ||||||
| 					form(method="get" action="/u").pair-entry | 					form(method="get" action="/u").pair-entry | ||||||
| 						input(type="text" name="u" placeholder="Username or URL").text | 						input(type="text" name="u" placeholder=ll.go_username_or_url).text | ||||||
| 						input(type="submit" value="Go").button | 						input(type="submit" value=ll.go_button).button | ||||||
| 				section | 				section | ||||||
| 					h2.title Go to post | 					h2.title= ll.go_to_post | ||||||
| 					form(method="get" action="/p").pair-entry | 					form(method="get" action="/p").pair-entry | ||||||
| 						input(type="text" name="p" placeholder="Shortcode or URL").text | 						input(type="text" name="p" placeholder=ll.go_shortcode_or_url).text | ||||||
| 						input(type="submit" value="Go").button | 						input(type="submit" value=ll.go_button).button | ||||||
| 
 | 
 | ||||||
| 		.about-container | 		.about-container | ||||||
| 			section.about | 			section.about | ||||||
| 				h2 About Bibliogram | 				h2= ll.about_bibliogram_header | ||||||
| 				p. | 				!= ll.pug_about_bibliogram_content(constants.featured_profiles.length) | ||||||
| 					Bibliogram is a website that takes data from Instagram's public profile views and puts it into | 				h2= ll.about_this_instance_header | ||||||
| 					a friendlier page that loads faster, gives downloadable images, eliminates ads, |  | ||||||
| 					generates RSS feeds, and doesn't urge you to sign up. #[a(href=(constants.featured_profiles.length ? "#featured-profiles" : "/u/instagram")).example-link See an example.] |  | ||||||
| 				p. |  | ||||||
| 					Bibliogram does #[em not] allow you to anonymously post, like, comment, follow, or view private profiles. |  | ||||||
| 					It does not preserve deleted posts. |  | ||||||
| 
 |  | ||||||
| 				h2 About this instance |  | ||||||
| 				ul | 				ul | ||||||
| 					if onionLocation | 					if onionLocation | ||||||
| 						li: a(href=onionLocation) Onion site available | 						li: a(href=onionLocation)= ll.onion_site_available | ||||||
| 					li: a(href=settingsReferrer) Settings | 					li: a(href=settingsReferrer)= ll.t_settings | ||||||
| 					if hasPrivacyPolicy | 					if hasPrivacyPolicy | ||||||
| 						li: a(href="/privacy") Privacy policy | 						li: a(href="/privacy")= ll.t_privacy_policy | ||||||
| 					else | 					else | ||||||
| 						li Owner has not written a privacy policy | 						li= ll.has_not_written_privacy_policy | ||||||
| 					if allUnblocked | 					if allUnblocked | ||||||
| 						li Instance is not blocked | 						li= ll.instance_not_blocked | ||||||
| 					else | 					else | ||||||
| 						li: a(href="https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instagram%20rate%20limits.md#tldr-what-does-it-mean-if-an-instance-is-blocked") Instance is partially blocked | 						li: a(href="https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instagram%20rate%20limits.md#tldr-what-does-it-mean-if-an-instance-is-blocked")= ll.instance_partially_blocked | ||||||
| 					li RSS feeds are #{rssEnabled ? "enabled" : "disabled"} | 					if rssEnabled | ||||||
|  | 						li= ll.rss_enabled | ||||||
|  | 					else | ||||||
|  | 						li= ll.rss_disabled | ||||||
| 
 | 
 | ||||||
| 				h2 External links | 				h2= ll.external_links_header | ||||||
| 				ul | 				ul | ||||||
| 					- | 					- | ||||||
| 						const links = [ | 						const links = [ | ||||||
| 							["https://sr.ht/~cadence/bibliogram/", "Code on sourcehut", "noopener noreferrer"], | 							["https://sr.ht/~cadence/bibliogram/", ll.source_link, "noopener noreferrer"], | ||||||
| 							["https://matrix.to/#/#bibliogram:matrix.org", "Discussion room on Matrix", "noopener noreferrer"], | 							["https://matrix.to/#/#bibliogram:matrix.org", ll.matrix_link, "noopener noreferrer"], | ||||||
| 							["https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instances.md", "Other Bibliogram instances", "noopener noreferrer"], | 							["https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instances.md", ll.instances_link, "noopener noreferrer"], | ||||||
| 							["https://cadence.moe/about/contact", "Contact the developer", "noopener noreferrer"] | 							["https://cadence.moe/about/contact", ll.contact_link, "noopener noreferrer"] | ||||||
| 						] | 						] | ||||||
| 					each entry in links | 					each entry in links | ||||||
| 						li: a(href!=entry[0] target="_blank" rel=entry[2])= entry[1] | 						li: a(href!=entry[0] target="_blank" rel=entry[2])= entry[1] | ||||||
| 
 | 
 | ||||||
| 				if constants.featured_profiles.length | 				if constants.featured_profiles.length | ||||||
| 					.featured-profiles#featured-profiles | 					.featured-profiles#featured-profiles | ||||||
| 						h2.featured-profiles-header Featured profiles | 						h2.featured-profiles-header= ll.featured_profiles_header | ||||||
| 
 | 
 | ||||||
| 						table.featured-profile-table | 						table.featured-profile-table | ||||||
| 							tbody | 							tbody | ||||||
| @ -73,7 +71,5 @@ html | |||||||
| 										td= profile.description | 										td= profile.description | ||||||
| 
 | 
 | ||||||
| 						details | 						details | ||||||
| 							summary What's this? | 							summary= ll.featured_profiles_whats_this | ||||||
| 							.details-content | 							.details-content= ll.html_featured_profiles_disclaimer | ||||||
| 								p The owner of this website personally thinks that these profiles are interesting. |  | ||||||
| 								p These are not endorsements from the Bibliogram project. |  | ||||||
|  | |||||||
| @ -4,8 +4,14 @@ mixin next_page_button(user, selectedTimeline, url, type) | |||||||
| 			- | 			- | ||||||
| 				const nu = new URL(url) | 				const nu = new URL(url) | ||||||
| 				nu.searchParams.set("page", selectedTimeline.pages.length+1) | 				nu.searchParams.set("page", selectedTimeline.pages.length+1) | ||||||
| 			a(href=`${nu.search}#page-${selectedTimeline.pages.length+1}` data-page=(selectedTimeline.pages.length+1) data-username=(user.data.username) data-type=type)#next-page.next-page Next page | 			a( | ||||||
|  | 				href=`${nu.search}#page-${selectedTimeline.pages.length+1}` | ||||||
|  | 				data-page=(selectedTimeline.pages.length+1) | ||||||
|  | 				data-username=(user.data.username) | ||||||
|  | 				data-type=type | ||||||
|  | 				data-loading-text=ll.next_page_button_loading | ||||||
|  | 			)#next-page.next-page= ll.next_page_button | ||||||
| 	else | 	else | ||||||
| 		div | 		div | ||||||
| 			div.page-number.no-more-pages | 			div.page-number.no-more-pages | ||||||
| 				span.number No more posts. | 				span.number= ll.no_more_posts_notice | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ mixin post(post, headerWithNavigation) | |||||||
| 								if caption | 								if caption | ||||||
| 									p.description= caption | 									p.description= caption | ||||||
| 							p.description | 							p.description | ||||||
| 								span Posted on #[time(datetime=post.date.toISOString() data-local-date)= post.getDisplayDate()]. | 								span!= ll.pug_post_timestamp({post}) | ||||||
| 
 | 
 | ||||||
| 		section.images-gallery | 		section.images-gallery | ||||||
| 			for entry in post.children | 			for entry in post.children | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ mixin timeline_page(page, pageIndex) | |||||||
| 		- const pageNumber = pageIndex + 1 | 		- const pageNumber = pageIndex + 1 | ||||||
| 		if pageNumber > 1 | 		if pageNumber > 1 | ||||||
| 			header.page-number(id=`page-${pageNumber}`) | 			header.page-number(id=`page-${pageNumber}`) | ||||||
| 				span.number Page #{pageNumber} | 				span.number= ll.fn_page_divider(pageNumber) | ||||||
| 
 | 
 | ||||||
| 		.timeline-inner(class=`${settings.timeline_columns}-columns`) | 		.timeline-inner(class=`${settings.timeline_columns}-columns`) | ||||||
| 			- const suggestedSize = 260 //- from css :( | 			- const suggestedSize = 260 //- from css :( | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| //- Needs website_origin, title, post, settings | //- Needs website_origin, title, post, settings | ||||||
| 
 | 
 | ||||||
|  | - const ll = lang.get(settings.language) | ||||||
|  | 
 | ||||||
| include includes/post | include includes/post | ||||||
| 
 | 
 | ||||||
| doctype html | doctype html | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ html | |||||||
| 				h1 Settings | 				h1 Settings | ||||||
| 
 | 
 | ||||||
| 				+fieldset("Features") | 				+fieldset("Features") | ||||||
| 					+select("language", "Language", true, [ | 					+select("language", "Language", false, [ | ||||||
| 						{value: "en", text: "English (International)"}, | 						{value: "en", text: "English (International)"}, | ||||||
| 						{value: "en-us", text: "English (US)"} | 						{value: "en-us", text: "English (US)"} | ||||||
| 					]) | 					]) | ||||||
|  | |||||||
| @ -5,13 +5,14 @@ include includes/next_page_button.pug | |||||||
| include includes/display_structured | include includes/display_structured | ||||||
| include includes/feed_link | include includes/feed_link | ||||||
| 
 | 
 | ||||||
|  | - const ll = lang.get(settings.language) | ||||||
| - const numberFormat = new Intl.NumberFormat().format | - const numberFormat = new Intl.NumberFormat().format | ||||||
| 
 | 
 | ||||||
| mixin selector-button(text, selectorType, urlSuffix) | mixin selector-button(text, selectorType, urlSuffix) | ||||||
| 	a(href=(type !== selectorType && `/u/${user.data.username}${urlSuffix}`) class=(type === selectorType && "active")).selector= text | 	a(href=(type !== selectorType && `/u/${user.data.username}${urlSuffix}`) class=(type === selectorType && "active")).selector= text | ||||||
| 
 | 
 | ||||||
| mixin verified-badge(classes) | mixin verified-badge(classes) | ||||||
| 	img.verified-badge(class=classes src=getStaticURL("html", "/static/img/verified.svg") width=19 height=19 alt="Verified." title="Verified") | 	img.verified-badge(class=classes src=getStaticURL("html", "/static/img/verified.svg") width=19 height=19 alt=ll.verified_badge_alt title=ll.verified_badge_title) | ||||||
| 
 | 
 | ||||||
| doctype html | doctype html | ||||||
| html | html | ||||||
| @ -42,7 +43,7 @@ html | |||||||
| 			a(href="/").nav-icon-link | 			a(href="/").nav-icon-link | ||||||
| 				img(src="/static/img/logo-circle-min.svg" alt="Bibliogram").logo | 				img(src="/static/img/logo-circle-min.svg" alt="Bibliogram").logo | ||||||
| 			a(href=settingsReferrer).nav-icon-link | 			a(href=settingsReferrer).nav-icon-link | ||||||
| 				img(src="/static/img/settings.svg" alt="Settings").settings | 				img(src="/static/img/settings.svg" alt=ll.t_settings).settings | ||||||
| 		.main-divider | 		.main-divider | ||||||
| 			header.profile-overview | 			header.profile-overview | ||||||
| 				.profile-sticky | 				.profile-sticky | ||||||
| @ -71,12 +72,18 @@ html | |||||||
| 							p.website | 							p.website | ||||||
| 								a(href=userURL)= userURL | 								a(href=userURL)= userURL | ||||||
| 						if user.posts != undefined | 						if user.posts != undefined | ||||||
| 							div.profile-counter #[span(data-numberformat=user.posts).count #{numberFormat(user.posts)}] posts | 							div.profile-counter | ||||||
|  | 								| #[span(data-numberformat=user.posts).count #{numberFormat(user.posts)} ] | ||||||
|  | 								= ll.post_counter_label | ||||||
| 						if followerCountsAvailable | 						if followerCountsAvailable | ||||||
| 							if user.following != undefined | 							if user.following != undefined | ||||||
| 								div.profile-counter #[span(data-numberformat=user.following).count #{numberFormat(user.following)}] following | 								div.profile-counter | ||||||
|  | 									| #[span(data-numberformat=user.following).count #{numberFormat(user.following)} ] | ||||||
|  | 									= ll.outgoing_follows_counter_label | ||||||
| 							if user.followedBy != undefined | 							if user.followedBy != undefined | ||||||
| 								div.profile-counter #[span(data-numberformat=user.followedBy).count #{numberFormat(user.followedBy)}] followed by | 								div.profile-counter | ||||||
|  | 									| #[span(data-numberformat=user.followedBy).count #{numberFormat(user.followedBy)} ] | ||||||
|  | 									= ll.incoming_follows_counter_label | ||||||
| 						else | 						else | ||||||
| 							div.profile-counter.not-available Followers not available. | 							div.profile-counter.not-available Followers not available. | ||||||
| 						.links | 						.links | ||||||
| @ -86,15 +93,15 @@ html | |||||||
| 							a(rel="noreferrer noopener" href=`https://www.instagram.com/${user.data.username}` target="_blank") instagram.com | 							a(rel="noreferrer noopener" href=`https://www.instagram.com/${user.data.username}` target="_blank") instagram.com | ||||||
| 					section.bibliogram-meta | 					section.bibliogram-meta | ||||||
| 						.links | 						.links | ||||||
| 							a(href="/") Home | 							a(href="/")= ll.t_home | ||||||
| 							a(href=settingsReferrer) Settings | 							a(href=settingsReferrer)= ll.t_settings | ||||||
| 
 | 
 | ||||||
| 			- const hasPosts = !user.data.is_private && selectedTimeline.pages.length && selectedTimeline.pages[0].length | 			- const hasPosts = !user.data.is_private && selectedTimeline.pages.length && selectedTimeline.pages[0].length | ||||||
| 			.timeline-section | 			.timeline-section | ||||||
| 				.selector-container | 				.selector-container | ||||||
| 					+selector-button("Timeline", "timeline", "") | 					+selector-button(ll.tab_timeline, "timeline", "") | ||||||
| 					if user.data.has_channel !== false | 					if user.data.has_channel !== false | ||||||
| 						+selector-button("IGTV", "igtv", "/channel") | 						+selector-button(ll.tab_igtv, "igtv", "/channel") | ||||||
| 
 | 
 | ||||||
| 				main(class=hasPosts ? "" : "no-posts")#timeline.timeline | 				main(class=hasPosts ? "" : "no-posts")#timeline.timeline | ||||||
| 					if hasPosts | 					if hasPosts | ||||||
| @ -106,6 +113,6 @@ html | |||||||
| 							div.page-number | 							div.page-number | ||||||
| 								span.number | 								span.number | ||||||
| 									if user.data.is_private | 									if user.data.is_private | ||||||
| 										| Profile is private. | 										= ll.profile_is_private_notice | ||||||
| 									else | 									else | ||||||
| 										| No posts. | 										= ll.no_posts_notice | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| const {Pinski} = require("pinski") | const {Pinski} = require("pinski") | ||||||
| const {subdirs} = require("node-dir") | const {subdirs} = require("node-dir") | ||||||
| const constants = require("../lib/constants") | const constants = require("../lib/constants") | ||||||
|  | const lang = require("../lang") | ||||||
| 
 | 
 | ||||||
| const passthrough = require("./passthrough") | const passthrough = require("./passthrough") | ||||||
| 
 | 
 | ||||||
| @ -64,7 +65,7 @@ subdirs("pug", async (err, dirs) => { | |||||||
| 
 | 
 | ||||||
| 	const plugins = require("pinski/plugins") | 	const plugins = require("pinski/plugins") | ||||||
| 	plugins.setInstance(pinski) | 	plugins.setInstance(pinski) | ||||||
| 	Object.assign(pinski.pugDefaultLocals, {constants}) | 	Object.assign(pinski.pugDefaultLocals, {constants, lang}) | ||||||
| 	Object.assign(passthrough, pinski.getExports()) | 	Object.assign(passthrough, pinski.getExports()) | ||||||
| 
 | 
 | ||||||
| 	console.log("[.] Server started") | 	console.log("[.] Server started") | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user