mirror of
				https://git.sr.ht/~cadence/bibliogram
				synced 2025-10-31 03:25:36 +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": { | ||||
|     "start": "cd src/site && node server.js", | ||||
|     "assistant": "cd src/site && node assistant.js", | ||||
|     "build-lang": "cd src/lang/utils && node build_base.js", | ||||
|     "test": "tap" | ||||
|   }, | ||||
|   "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: { | ||||
| 		language: "en", | ||||
| 		rewrite_youtube: "invidio.us", | ||||
| 		rewrite_twitter: "nitter.net" | ||||
| 	}, | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| const constants = require("../../lib/constants") | ||||
| const lang = require("../../lang") | ||||
| const switcher = require("../../lib/utils/torswitcher") | ||||
| const {fetchUser, getOrFetchShortcode, userRequestCache, history, assistantSwitcher} = require("../../lib/collectors") | ||||
| const {render, redirect, getStaticURL} = require("pinski/plugins") | ||||
| @ -112,7 +113,8 @@ module.exports = [ | ||||
| 							username, | ||||
| 							expiresMinutes: userRequestCache.getTtl("user/"+username, 1000*60), | ||||
| 							getStaticURL, | ||||
| 							settings | ||||
| 							settings, | ||||
| 							lang | ||||
| 						}) | ||||
| 					} | ||||
| 				} else if (error === constants.symbols.extractor_results.AGE_RESTRICTED) { | ||||
| @ -179,7 +181,7 @@ module.exports = [ | ||||
| 					contentType: "application/json", | ||||
| 					content: { | ||||
| 						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 => { | ||||
|  | ||||
| @ -57,7 +57,7 @@ class ElemJS { | ||||
| 	event(name, callback) { | ||||
| 		this.element.addEventListener(name, event => callback(event)) | ||||
| 	} | ||||
| 	child(toAdd, position) { | ||||
| 	child(toAdd, position = undefined) { | ||||
| 		if (typeof(toAdd) == "object") { | ||||
| 			toAdd.parent = this; | ||||
| 			if (typeof(position) == "number" && position >= 0) { | ||||
|  | ||||
| @ -75,7 +75,7 @@ class NextPage extends FreezeWidth { | ||||
| 	fetch() { | ||||
| 		if (this.fetching) return | ||||
| 		this.fetching = true | ||||
| 		this.freeze("Loading...") | ||||
| 		this.freeze(this.element.getAttribute("data-loading-text")) | ||||
| 		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 => { | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| //- Needs post | ||||
| 
 | ||||
| - const ll = lang.get(settings.language) | ||||
| 
 | ||||
| include ../includes/post.pug | ||||
| 
 | ||||
| +post(post, true) | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| //- Needs user, selectedTimeline, url, type | ||||
| 
 | ||||
| - const ll = lang.get(settings.language) | ||||
| 
 | ||||
| include ../includes/timeline_page.pug | ||||
| include ../includes/next_page_button.pug | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| //- Needs rssEnabled, allUnblocked, torAvailable, hasPrivacyPolicy, onionLocation | ||||
| 
 | ||||
| - const ll = lang.get(settings.language) | ||||
| 
 | ||||
| doctype html | ||||
| html | ||||
| 	head | ||||
| @ -13,57 +15,53 @@ html | ||||
| 		.go-sections-container | ||||
| 			.go-sections | ||||
| 				section | ||||
| 					h2.title Go to profile | ||||
| 					h2.title= ll.go_to_profile | ||||
| 					form(method="get" action="/u").pair-entry | ||||
| 						input(type="text" name="u" placeholder="Username or URL").text | ||||
| 						input(type="submit" value="Go").button | ||||
| 						input(type="text" name="u" placeholder=ll.go_username_or_url).text | ||||
| 						input(type="submit" value=ll.go_button).button | ||||
| 				section | ||||
| 					h2.title Go to post | ||||
| 					h2.title= ll.go_to_post | ||||
| 					form(method="get" action="/p").pair-entry | ||||
| 						input(type="text" name="p" placeholder="Shortcode or URL").text | ||||
| 						input(type="submit" value="Go").button | ||||
| 						input(type="text" name="p" placeholder=ll.go_shortcode_or_url).text | ||||
| 						input(type="submit" value=ll.go_button).button | ||||
| 
 | ||||
| 		.about-container | ||||
| 			section.about | ||||
| 				h2 About Bibliogram | ||||
| 				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=(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 | ||||
| 				h2= ll.about_bibliogram_header | ||||
| 				!= ll.pug_about_bibliogram_content(constants.featured_profiles.length) | ||||
| 				h2= ll.about_this_instance_header | ||||
| 				ul | ||||
| 					if onionLocation | ||||
| 						li: a(href=onionLocation) Onion site available | ||||
| 					li: a(href=settingsReferrer) Settings | ||||
| 						li: a(href=onionLocation)= ll.onion_site_available | ||||
| 					li: a(href=settingsReferrer)= ll.t_settings | ||||
| 					if hasPrivacyPolicy | ||||
| 						li: a(href="/privacy") Privacy policy | ||||
| 						li: a(href="/privacy")= ll.t_privacy_policy | ||||
| 					else | ||||
| 						li Owner has not written a privacy policy | ||||
| 						li= ll.has_not_written_privacy_policy | ||||
| 					if allUnblocked | ||||
| 						li Instance is not blocked | ||||
| 						li= ll.instance_not_blocked | ||||
| 					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 RSS feeds are #{rssEnabled ? "enabled" : "disabled"} | ||||
| 						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 | ||||
| 					if rssEnabled | ||||
| 						li= ll.rss_enabled | ||||
| 					else | ||||
| 						li= ll.rss_disabled | ||||
| 
 | ||||
| 				h2 External links | ||||
| 				h2= ll.external_links_header | ||||
| 				ul | ||||
| 					- | ||||
| 						const links = [ | ||||
| 							["https://sr.ht/~cadence/bibliogram/", "Code on sourcehut", "noopener noreferrer"], | ||||
| 							["https://matrix.to/#/#bibliogram:matrix.org", "Discussion room on Matrix", "noopener noreferrer"], | ||||
| 							["https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instances.md", "Other Bibliogram instances", "noopener noreferrer"], | ||||
| 							["https://cadence.moe/about/contact", "Contact the developer", "noopener noreferrer"] | ||||
| 							["https://sr.ht/~cadence/bibliogram/", ll.source_link, "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", ll.instances_link, "noopener noreferrer"], | ||||
| 							["https://cadence.moe/about/contact", ll.contact_link, "noopener noreferrer"] | ||||
| 						] | ||||
| 					each entry in links | ||||
| 						li: a(href!=entry[0] target="_blank" rel=entry[2])= entry[1] | ||||
| 
 | ||||
| 				if constants.featured_profiles.length | ||||
| 					.featured-profiles#featured-profiles | ||||
| 						h2.featured-profiles-header Featured profiles | ||||
| 						h2.featured-profiles-header= ll.featured_profiles_header | ||||
| 
 | ||||
| 						table.featured-profile-table | ||||
| 							tbody | ||||
| @ -73,7 +71,5 @@ html | ||||
| 										td= profile.description | ||||
| 
 | ||||
| 						details | ||||
| 							summary What's this? | ||||
| 							.details-content | ||||
| 								p The owner of this website personally thinks that these profiles are interesting. | ||||
| 								p These are not endorsements from the Bibliogram project. | ||||
| 							summary= ll.featured_profiles_whats_this | ||||
| 							.details-content= ll.html_featured_profiles_disclaimer | ||||
|  | ||||
| @ -4,8 +4,14 @@ mixin next_page_button(user, selectedTimeline, url, type) | ||||
| 			- | ||||
| 				const nu = new URL(url) | ||||
| 				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 | ||||
| 		div | ||||
| 			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 | ||||
| 									p.description= caption | ||||
| 							p.description | ||||
| 								span Posted on #[time(datetime=post.date.toISOString() data-local-date)= post.getDisplayDate()]. | ||||
| 								span!= ll.pug_post_timestamp({post}) | ||||
| 
 | ||||
| 		section.images-gallery | ||||
| 			for entry in post.children | ||||
|  | ||||
| @ -5,7 +5,7 @@ mixin timeline_page(page, pageIndex) | ||||
| 		- const pageNumber = pageIndex + 1 | ||||
| 		if pageNumber > 1 | ||||
| 			header.page-number(id=`page-${pageNumber}`) | ||||
| 				span.number Page #{pageNumber} | ||||
| 				span.number= ll.fn_page_divider(pageNumber) | ||||
| 
 | ||||
| 		.timeline-inner(class=`${settings.timeline_columns}-columns`) | ||||
| 			- const suggestedSize = 260 //- from css :( | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| //- Needs website_origin, title, post, settings | ||||
| 
 | ||||
| - const ll = lang.get(settings.language) | ||||
| 
 | ||||
| include includes/post | ||||
| 
 | ||||
| doctype html | ||||
|  | ||||
| @ -45,7 +45,7 @@ html | ||||
| 				h1 Settings | ||||
| 
 | ||||
| 				+fieldset("Features") | ||||
| 					+select("language", "Language", true, [ | ||||
| 					+select("language", "Language", false, [ | ||||
| 						{value: "en", text: "English (International)"}, | ||||
| 						{value: "en-us", text: "English (US)"} | ||||
| 					]) | ||||
|  | ||||
| @ -5,13 +5,14 @@ include includes/next_page_button.pug | ||||
| include includes/display_structured | ||||
| include includes/feed_link | ||||
| 
 | ||||
| - const ll = lang.get(settings.language) | ||||
| - const numberFormat = new Intl.NumberFormat().format | ||||
| 
 | ||||
| mixin selector-button(text, selectorType, urlSuffix) | ||||
| 	a(href=(type !== selectorType && `/u/${user.data.username}${urlSuffix}`) class=(type === selectorType && "active")).selector= text | ||||
| 
 | ||||
| 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 | ||||
| html | ||||
| @ -42,7 +43,7 @@ html | ||||
| 			a(href="/").nav-icon-link | ||||
| 				img(src="/static/img/logo-circle-min.svg" alt="Bibliogram").logo | ||||
| 			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 | ||||
| 			header.profile-overview | ||||
| 				.profile-sticky | ||||
| @ -71,12 +72,18 @@ html | ||||
| 							p.website | ||||
| 								a(href=userURL)= userURL | ||||
| 						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 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 | ||||
| 								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 | ||||
| 							div.profile-counter.not-available Followers not available. | ||||
| 						.links | ||||
| @ -86,15 +93,15 @@ html | ||||
| 							a(rel="noreferrer noopener" href=`https://www.instagram.com/${user.data.username}` target="_blank") instagram.com | ||||
| 					section.bibliogram-meta | ||||
| 						.links | ||||
| 							a(href="/") Home | ||||
| 							a(href=settingsReferrer) Settings | ||||
| 							a(href="/")= ll.t_home | ||||
| 							a(href=settingsReferrer)= ll.t_settings | ||||
| 
 | ||||
| 			- const hasPosts = !user.data.is_private && selectedTimeline.pages.length && selectedTimeline.pages[0].length | ||||
| 			.timeline-section | ||||
| 				.selector-container | ||||
| 					+selector-button("Timeline", "timeline", "") | ||||
| 					+selector-button(ll.tab_timeline, "timeline", "") | ||||
| 					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 | ||||
| 					if hasPosts | ||||
| @ -106,6 +113,6 @@ html | ||||
| 							div.page-number | ||||
| 								span.number | ||||
| 									if user.data.is_private | ||||
| 										| Profile is private. | ||||
| 										= ll.profile_is_private_notice | ||||
| 									else | ||||
| 										| No posts. | ||||
| 										= ll.no_posts_notice | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| const {Pinski} = require("pinski") | ||||
| const {subdirs} = require("node-dir") | ||||
| const constants = require("../lib/constants") | ||||
| const lang = require("../lang") | ||||
| 
 | ||||
| const passthrough = require("./passthrough") | ||||
| 
 | ||||
| @ -64,7 +65,7 @@ subdirs("pug", async (err, dirs) => { | ||||
| 
 | ||||
| 	const plugins = require("pinski/plugins") | ||||
| 	plugins.setInstance(pinski) | ||||
| 	Object.assign(pinski.pugDefaultLocals, {constants}) | ||||
| 	Object.assign(pinski.pugDefaultLocals, {constants, lang}) | ||||
| 	Object.assign(passthrough, pinski.getExports()) | ||||
| 
 | ||||
| 	console.log("[.] Server started") | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user