diff --git a/art/README.md b/art/README.md index 66227b4f..c2f74048 100644 --- a/art/README.md +++ b/art/README.md @@ -28,5 +28,7 @@ * `logo-readme.svg` 是 `logo.svg` 的 `for-repo-readme` 图层粘贴过来再去掉那个底部矩形的结果。 * `logo-readme.png` 是 `logo-readme.svg` 用 Inkscape 导出后 `pngcrush` 的结果。 * `logo-site.svg` 是将 `logo.svg` 的 `chinese` 图层的内容挪到 32x144 画布另存为的结果。 +* `logo-site-en.svg` 是将 `logo.svg` 的 `english title` 图层的内容挪到 32x252 画布另存为的结果。 * `static/img/logo.svg` 是在 Inkscape 中另存 `logo-site.svg` 为优化的 SVG 的结果。 +* `static/img/logo.svg` 是在 Inkscape 中另存 `logo-site-en.svg` 为优化的 SVG 的结果。 * `static/img/favicon.ico` 是在 Gimp 将 `favicon.xcf` 先合并为单个图层再保存为 Windows ICO 格式的结果。 diff --git a/art/logo-site-en.svg b/art/logo-site-en.svg new file mode 100644 index 00000000..8c477e5f --- /dev/null +++ b/art/logo-site-en.svg @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 953e48fb..9cadfc32 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -35,9 +35,48 @@ const commonRehypePlugins = [ ], ] +// i18n workarounds +// see https://github.com/facebook/docusaurus/issues/4542 +// and https://github.com/facebook/docusaurus/pull/8677 +const defaultLocale = 'zh-Hans' +process.env.DOCUSAURUS_CURRENT_LOCALE ??= defaultLocale +if (process.env.DOCUSAURUS_CURRENT_LOCALE == 'undefined') { + // this is also happening for some reason... + process.env.DOCUSAURUS_CURRENT_LOCALE = defaultLocale +} + +const configStringsTranslations = { + en: { + title: 'AREWELOONGYET?', + tagline: 'Your one-stop portal for following LoongArch upstream work.', + logoPath: 'img/logo-en.svg', + twilTitle: 'This Week in LoongArch', + twilDescription: + 'This Week in LoongArch (TWiL) is a weekly newsletter covering the latest developments in the LoongArch community, publishing every Monday.', + siteNewsTitle: 'Site news', + siteNewsDescription: + 'News on the development and operations of the AREWELOONGYET? site.', + }, + 'zh-Hans': { + title: '咱龙了吗?', + tagline: '一站式了解 LoongArch 的上游生态建设。', + logoPath: 'img/logo.svg', + twilTitle: '每周一龙', + twilDescription: + '龙架构新世界生态建设周报《每周一龙》(This Week in LoongArch)。每周一都为大家报道 LoongArch 社区最前线的第一手新鲜资讯!', + siteNewsTitle: '本站动态', + siteNewsDescription: '《咱龙了吗?》网站本身的开发、运营动态。', + }, +} + +const localizeConfigString = (key: string) => { + const locale = process.env.DOCUSAURUS_CURRENT_LOCALE ?? defaultLocale + return configStringsTranslations[locale][key] +} + const config: Config = { - title: '咱龙了吗?', - tagline: '一站式了解 LoongArch 的上游生态建设。', + title: localizeConfigString('title'), + tagline: localizeConfigString('tagline'), url: 'https://areweloongyet.com', baseUrl: '/', onBrokenLinks: 'throw', @@ -55,8 +94,8 @@ const config: Config = { // metadata like html lang. For example, if your site is Chinese, you may want // to replace "en" with "zh-Hans". i18n: { - defaultLocale: 'zh-Hans', - locales: ['zh-Hans'], + defaultLocale: defaultLocale, + locales: [defaultLocale, 'en'], }, future: { @@ -157,9 +196,8 @@ const config: Config = { id: 'blog-newsletter', routeBasePath: 'newsletter', path: './newsletter', - blogTitle: '每周一龙', - blogDescription: - '龙架构新世界生态建设周报《每周一龙》(This Week in LoongArch)。每周一都为大家报道 LoongArch 社区最前线的第一手新鲜资讯!', + blogTitle: localizeConfigString('twilTitle'), + blogDescription: localizeConfigString('twilDescription'), editUrl: 'https://github.com/loongson-community/areweloongyet/edit/main', beforeDefaultRemarkPlugins: commonBeforeDefaultRemarkPlugins, @@ -182,8 +220,8 @@ const config: Config = { rehypePlugins: commonRehypePlugins, }, blog: { - blogTitle: '本站动态', - blogDescription: '《咱龙了吗?》网站本身的开发、运营动态。', + blogTitle: localizeConfigString('siteNewsTitle'), + blogDescription: localizeConfigString('siteNewsDescription'), showReadingTime: true, // Please change this to your repo. // Remove this to remove the "edit this page" links. @@ -221,7 +259,7 @@ const config: Config = { title: '', logo: { alt: 'My Site Logo', - src: 'img/logo.svg', + src: localizeConfigString('logoPath'), }, items: [ { @@ -235,9 +273,14 @@ const config: Config = { { to: '/blog', label: '本站动态', position: 'left' }, { href: 'https://github.com/loongson-community/areweloongyet', - label: 'GitHub', position: 'right', + // use the GitHub icon instead of text for the link (for desktop + // displays), to conserve horizontal space + // https://github.com/facebook/docusaurus/issues/2634 + className: 'header--github-link', + 'aria-label': 'GitHub repository of the website', }, + { type: 'localeDropdown', position: 'right' }, ], }, footer: { diff --git a/i18n/en/code.json b/i18n/en/code.json new file mode 100644 index 00000000..b3bee6bf --- /dev/null +++ b/i18n/en/code.json @@ -0,0 +1,401 @@ +{ + "theme.ErrorPageContent.title": { + "message": "This page crashed.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.NotFound.title": { + "message": "Page Not Found", + "description": "The title of the 404 page" + }, + "theme.NotFound.p1": { + "message": "We could not find what you were looking for.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.admonition.note": { + "message": "note", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.danger": { + "message": "danger", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "info", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.caution": { + "message": "caution", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Scroll back to top", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archive", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archive", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Blog list page navigation", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Newer Entries", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Older Entries", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Blog post page navigation", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Newer Post", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Older Post", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "One post|{count} posts", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} tagged with \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "View All Tags", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Switch between dark and light mode (currently {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "dark mode", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "light mode", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Docs pages", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Previous", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Next", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "One doc tagged|{count} docs tagged", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} with \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "Version: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "latest version", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "Edit this page", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Direct link to {heading}", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " on {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " by {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Last updated{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versions", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Tags:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Close", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Blog recent posts navigation", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copied", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copy code to clipboard", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copy", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "Toggle the collapsible sidebar category '{label}'", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.NavBar.navAriaLabel": { + "message": "Main", + "description": "The ARIA label for the main navigation" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Languages", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "On this page", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readMore": { + "message": "Read More", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Read more about {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "One min read|{readingTime} min read", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.breadcrumbs.home": { + "message": "Home page", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.navAriaLabel": { + "message": "Docs sidebar", + "description": "The ARIA label for the sidebar navigation" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Back to main menu", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Try again", + "description": "The label of the button to try again rendering when the React error boundary captures an error" + }, + "theme.common.skipToMainContent": { + "message": "Skip to main content", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Tags", + "description": "The title of the tag list page" + }, + "theme.docs.DocCard.categoryDescription.plurals": { + "message": "1 item|{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.admonition.warning": { + "message": "warning", + "description": "The default label used for the Warning admonition (:::warning)" + }, + "theme.DocSidebarItem.expandCategoryAriaLabel": { + "message": "Expand sidebar category '{label}'", + "description": "The ARIA label to expand the sidebar category" + }, + "theme.DocSidebarItem.collapseCategoryAriaLabel": { + "message": "Collapse sidebar category '{label}'", + "description": "The ARIA label to collapse the sidebar category" + }, + "theme.SearchBar.noResultsText": { + "message": "No results" + }, + "theme.SearchBar.seeAllOutsideContext": { + "message": "See all results outside \"{context}\"" + }, + "theme.SearchBar.searchInContext": { + "message": "See all results within \"{context}\"" + }, + "theme.SearchBar.seeAll": { + "message": "See all results" + }, + "theme.SearchBar.label": { + "message": "Search", + "description": "The ARIA label and placeholder for search button" + }, + "theme.SearchPage.existingResultsTitle": { + "message": "Search results for \"{query}\"", + "description": "The search page title for non-empty query" + }, + "theme.SearchPage.emptyResultsTitle": { + "message": "Search the documentation", + "description": "The search page title for empty query" + }, + "theme.SearchPage.searchContext.everywhere": { + "message": "Everywhere" + }, + "theme.SearchPage.documentsFound.plurals": { + "message": "1 document found|{count} documents found", + "description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.SearchPage.noResultsText": { + "message": "No documents were found", + "description": "The paragraph for empty search result" + }, + "theme.blog.author.pageTitle": { + "message": "{authorName} - {nPosts}", + "description": "The title of the page for a blog author" + }, + "theme.blog.authorsList.pageTitle": { + "message": "Authors", + "description": "The title of the authors page" + }, + "theme.blog.authorsList.viewAll": { + "message": "View all authors", + "description": "The label of the link targeting the blog authors page" + }, + "theme.blog.author.noPosts": { + "message": "This author has not written any posts yet.", + "description": "The text for authors with 0 blog post" + }, + "theme.contentVisibility.unlistedBanner.title": { + "message": "Unlisted page", + "description": "The unlisted content banner title" + }, + "theme.contentVisibility.unlistedBanner.message": { + "message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", + "description": "The unlisted content banner message" + }, + "theme.contentVisibility.draftBanner.title": { + "message": "Draft page", + "description": "The draft content banner title" + }, + "theme.contentVisibility.draftBanner.message": { + "message": "This page is a draft. It will only be visible in dev and be excluded from the production build.", + "description": "The draft content banner message" + }, + "第一个“形如此”之前的内容(对汉语而言为空)": { + "message": "Links " + }, + "形如此": { + "message": "like this" + }, + "第一个与第二个“形如此”之间的内容(对汉语而言为“的链接,或部分”)": { + "message": " or some " + }, + "第二个“形如此”之后的内容(对汉语而言为“的链接,其目标主机可能位于中国大陆境外。”)": { + "message": " may point to hosts outside of the Chinese mainland." + }, + "如需跟进阅读却无法访问,请自行寻找解决方案。": { + "message": " You are on your own in case of connectivity problems." + }, + "awly.homepage.title": { + "message": "Main page", + "description": "Title of the homepage" + }, + "awly.homepage.description": { + "message": "Your one-stop portal for following LoongArch upstream work", + "description": "Description of the homepage" + }, + "awly.homepage.slogan": { + "message": "ONTHEWAY!", + "description": "Slogan of the homepage" + }, + "Linux 开源生态的多数基础组件,其最新版本都已包含 LoongArch 支持了。": { + "message": "LoongArch support has entered most parts of the open-source Linux infrastructure." + }, + "尽管龙芯公司仍未公开 LoongArch 手册的其余部分,但大量公开信息如 QEMU、内核适配已将相关指令编码与具体行为尽数披露,手册的缺失已不再能阻碍优化的脚步。": { + "message": "Even though the Loongson Corporation has yet to release the remaining volumes of the LoongArch manual, thanks to a wealth of public information such as publicly available QEMU and Linux changes, \"undisclosed\" information such as instruction encoding and behaviors is, in effect, already public. The absence of manuals can no longer hinder optimization efforts by The People." + }, + "我们预计将在 2023~2024 看到 LoongArch 新世界生态的突飞猛进,有您的参与会更快些。": { + "message": "We expect to see the LoongArch new-world ecosystem evolve rapidly in the 2023-2024 period; it will be even faster with YOU." + }, + "本站由社区建设维护,": { + "message": "This site is community-maintained. " + }, + "欢迎来坐坐": { + "message": "Welcome aboard" + }, + "!": { + "message": "!" + } +} diff --git a/i18n/en/docusaurus-plugin-content-blog-blog-newsletter/options.json b/i18n/en/docusaurus-plugin-content-blog-blog-newsletter/options.json new file mode 100644 index 00000000..9239ff70 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-blog-blog-newsletter/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Recent posts", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/en/docusaurus-plugin-content-blog/options.json b/i18n/en/docusaurus-plugin-content-blog/options.json new file mode 100644 index 00000000..9239ff70 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Recent posts", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs/current.json b/i18n/en/docusaurus-plugin-content-docs/current.json new file mode 100644 index 00000000..1357c260 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,10 @@ +{ + "version.label": { + "message": "Next", + "description": "The label for version current" + }, + "sidebar.main.category.参与本站开发": { + "message": "Contributing", + "description": "The label for category 参与本站开发 in sidebar main" + } +} diff --git a/i18n/en/docusaurus-theme-classic/footer.json b/i18n/en/docusaurus-theme-classic/footer.json new file mode 100644 index 00000000..e7a04f51 --- /dev/null +++ b/i18n/en/docusaurus-theme-classic/footer.json @@ -0,0 +1,82 @@ +{ + "link.title.站内导航": { + "message": "Quick links", + "description": "The title of the footer links column with title=站内导航 in the footer" + }, + "link.title.社区资源": { + "message": "Community resources", + "description": "The title of the footer links column with title=社区资源 in the footer" + }, + "link.title.企业资源": { + "message": "Enterprise resources", + "description": "The title of the footer links column with title=企业资源 in the footer" + }, + "link.item.label.每周一龙": { + "message": "TWiL newsletter", + "description": "The label of footer link with label=每周一龙 linking to /newsletter" + }, + "link.item.label.本站动态": { + "message": "Site news", + "description": "The label of footer link with label=本站动态 linking to /blog" + }, + "link.item.label.龙芯社区 GitHub": { + "message": "Loongson Community GitHub", + "description": "The label of footer link with label=龙芯社区 GitHub linking to https://github.com/loongson-community" + }, + "link.item.label.LoongArch 非官方论坛": { + "message": "LoongArch unofficial forum", + "description": "The label of footer link with label=LoongArch 非官方论坛 linking to https://bbs.loongarch.org" + }, + "link.item.label.Telegram Loongson Group": { + "message": "Telegram Loongson Group", + "description": "The label of footer link with label=Telegram Loongson Group linking to https://t.me/loongson_users" + }, + "link.item.label.龙芯用户闲聊 Matrix 群": { + "message": "Matrix Loongson off-topic room", + "description": "The label of footer link with label=龙芯用户闲聊 Matrix 群 linking to https://matrix.to/#/%23loongson-users-ot:matrix.org" + }, + "link.item.label.百度贴吧龙芯吧": { + "message": "Baidu Tieba", + "description": "The label of footer link with label=百度贴吧龙芯吧 linking to https://tieba.baidu.com/f?kw=%E9%BE%99%E8%8A%AF&ie=utf-8" + }, + "link.item.label.龙芯公司官网": { + "message": "Loongson Corp. official site", + "description": "The label of footer link with label=龙芯公司官网 linking to https://loongson.cn" + }, + "link.item.label.龙芯公司官方 GitHub": { + "message": "Loongson official GitHub", + "description": "The label of footer link with label=龙芯公司官方 GitHub linking to https://github.com/loongson" + }, + "copyright": { + "message": "© 2022-2025 Project contributors. This project is unaffiliated with any for-profit entity.
All trademarks referenced herein are property of their respective holders.", + "description": "The footer copyright" + }, + "link.title.第三方维护资源": { + "message": "Third-party resources", + "description": "The title of the footer links column with title=第三方维护资源 in the footer" + }, + "link.title.官方维护资源": { + "message": "Official resources", + "description": "The title of the footer links column with title=官方维护资源 in the footer" + }, + "link.item.label.阅读材料": { + "message": "Reads", + "description": "The label of footer link with label=阅读材料 linking to /docs/intro" + }, + "link.item.label.汇编速查": { + "message": "AsmDB", + "description": "The label of footer link with label=汇编速查 linking to /asmdb" + }, + "link.item.label.跨社区协同 issue 页": { + "message": "Inter-community issue tracker", + "description": "The label of footer link with label=跨社区协同 issue 页 linking to https://github.com/loongson-community/discussions/issues" + }, + "link.item.label.龙友会(筹备中)": { + "message": "Loong Meetups (WIP)", + "description": "The label of footer link with label=龙友会(筹备中) linking to https://github.com/loongson-community/meetups" + }, + "link.item.label.龙芯爱好者社区": { + "message": "Loongson Amateurs Community", + "description": "The label of footer link with label=龙芯爱好者社区 linking to https://www.loongbbs.cn" + } +} diff --git a/i18n/en/docusaurus-theme-classic/navbar.json b/i18n/en/docusaurus-theme-classic/navbar.json new file mode 100644 index 00000000..3f0f3d53 --- /dev/null +++ b/i18n/en/docusaurus-theme-classic/navbar.json @@ -0,0 +1,26 @@ +{ + "logo.alt": { + "message": "Logo of AREWELOONGYET?", + "description": "The alt text of navbar logo" + }, + "item.label.阅读材料": { + "message": "Reads", + "description": "Navbar item with label 阅读材料" + }, + "item.label.每周一龙": { + "message": "TWiL", + "description": "Navbar item with label 每周一龙" + }, + "item.label.本站动态": { + "message": "News", + "description": "Navbar item with label 本站动态" + }, + "item.label.GitHub": { + "message": "GitHub", + "description": "Navbar item with label GitHub" + }, + "item.label.汇编速查": { + "message": "AsmDB", + "description": "Navbar item with label 汇编速查" + } +} diff --git a/i18n/zh-Hans/code.json b/i18n/zh-Hans/code.json new file mode 100644 index 00000000..4bc22319 --- /dev/null +++ b/i18n/zh-Hans/code.json @@ -0,0 +1,17 @@ +{ + "第一个“形如此”之前的内容(对汉语而言为空)": { + "message": "" + }, + "形如此": { + "message": "形如此" + }, + "第一个与第二个“形如此”之间的内容(对汉语而言为“的链接,或部分”)": { + "message": "的链接,或部分" + }, + "第二个“形如此”之后的内容(对汉语而言为“的链接,其目标主机可能位于中国大陆境外。”)": { + "message": "的链接,其目标主机可能位于中国大陆境外。" + }, + "如需跟进阅读却无法访问,请自行寻找解决方案。": { + "message": "如需跟进阅读却无法访问,请自行寻找解决方案。" + } +} diff --git a/src/css/custom.css b/src/css/custom.css index 7b01b748..f4865670 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -100,6 +100,9 @@ h6 { font-family: var(--ifm-font-family-base); font-style: normal; font-size: 1rem; + /* conserve horizontal space */ + /* some padding is already reserved by the navbarSearchContainer */ + margin-left: 0 !important; } .navbar__link { @@ -151,3 +154,67 @@ em.cjk { fill: context-stroke !important; stroke: context-stroke !important; } + +/* https://github.com/facebook/docusaurus/issues/2634 */ +@media screen and (max-width: 996px) { + .header--github-link::before { + content: 'GitHub'; + } +} + +@media screen and (min-width: 996px) { + .header--github-link { + width: 40px; + height: 40px; + padding: 10px; + margin-right: 4px; + border-radius: 50%; + transition: background var(--ifm-transition-fast); + } + + .header--github-link:hover { + background: var(--ifm-color-emphasis-200); + } + + .header--github-link::before { + content: ''; + height: 100%; + display: block; + /* https://stackoverflow.com/questions/21509982/change-svg-fill-color-in-before-or-after-css */ + mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; + mask-size: cover; + background-color: var(--ifm-navbar-link-color); + } +} + +/* for ToggleLikeDropdownNavbarItem */ +/* dropdown, but looking like a toggle when non-mobile */ + +@media screen and (min-width: 996px) { + .toggleLikeDropdown { + display: flex; + align-content: center; + align-items: center; + + width: 40px; + height: 40px; + padding: 10px; + margin-right: 4px; + border-radius: 50%; + transition: background var(--ifm-transition-fast); + } + + .toggleLikeDropdown:hover { + background: var(--ifm-color-emphasis-200); + } + + /* no dropdown arrow for the child for less horizontal space */ + .toggleLikeDropdown > .navbar__link::after { + display: none; + } + + .toggleLikeDropdown > .navbar__link:hover { + color: inherit; + } +} diff --git a/src/data/01-toolchain/index.yml b/src/data/01-toolchain/index.yml index 2cec5309..7edff952 100644 --- a/src/data/01-toolchain/index.yml +++ b/src/data/01-toolchain/index.yml @@ -1 +1,3 @@ -name: '工具链' +name: + en: 'Toolchains' + zh-Hans: '工具链' diff --git a/src/data/02-programming-languages/index.yml b/src/data/02-programming-languages/index.yml index 05675718..9063e85e 100644 --- a/src/data/02-programming-languages/index.yml +++ b/src/data/02-programming-languages/index.yml @@ -1 +1,3 @@ -name: '编程语言' +name: + en: 'Languages' + zh-Hans: '编程语言' diff --git a/src/data/03-kernel/index.yml b/src/data/03-kernel/index.yml index 1995e5ec..f5b82bde 100644 --- a/src/data/03-kernel/index.yml +++ b/src/data/03-kernel/index.yml @@ -1 +1,3 @@ -name: '内核' +name: + en: 'Kernels' + zh-Hans: '内核' diff --git a/src/data/04-fw-and-emulators/index.yml b/src/data/04-fw-and-emulators/index.yml index 5531115f..0abe6989 100644 --- a/src/data/04-fw-and-emulators/index.yml +++ b/src/data/04-fw-and-emulators/index.yml @@ -1 +1,3 @@ -name: '固件 & 模拟器' +name: + en: 'FW & Emulators' + zh-Hans: '固件 & 模拟器' diff --git a/src/data/06-jit-runtimes/index.yml b/src/data/06-jit-runtimes/index.yml index 122f1949..49fe1c8f 100644 --- a/src/data/06-jit-runtimes/index.yml +++ b/src/data/06-jit-runtimes/index.yml @@ -1 +1,3 @@ -name: 'JIT 运行时' +name: + en: 'JIT Runtimes' + zh-Hans: 'JIT 运行时' diff --git a/src/data/07-distros/index.yml b/src/data/07-distros/index.yml index 4e6b169b..7796fe04 100644 --- a/src/data/07-distros/index.yml +++ b/src/data/07-distros/index.yml @@ -1 +1,3 @@ -name: '发行版' +name: + en: 'Distributions' + zh-Hans: '发行版' diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 76a95fcd..c3628b2b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,29 +1,42 @@ import clsx from 'clsx' +import Translate, { translate } from '@docusaurus/Translate' import Layout from '@theme/Layout' import HomepageFeatures from '@site/src/components/HomepageFeatures' import styles from './index.module.css' function HomepageHeader() { + const slogan = translate({ + id: 'awly.homepage.slogan', + message: '行则将至!', + description: 'Slogan of the homepage', + }) + return (
-

行则将至!

+

{slogan}

- Linux 开源生态的多数基础组件,其最新版本都已包含 LoongArch 支持了。 + + Linux 开源生态的多数基础组件,其最新版本都已包含 LoongArch 支持了。 +
- 尽管龙芯公司仍未公开 LoongArch 手册的其余部分,但大量公开信息如 - QEMU、内核适配已将相关指令编码与具体行为尽数披露,手册的缺失已不再能阻碍优化的脚步。 + + 尽管龙芯公司仍未公开 LoongArch 手册的其余部分,但大量公开信息如 + QEMU、内核适配已将相关指令编码与具体行为尽数披露,手册的缺失已不再能阻碍优化的脚步。 +
- 我们预计将在 2023~2024 看到 LoongArch - 新世界生态的突飞猛进,有您的参与会更快些。 + + 我们预计将在 2023~2024 看到 LoongArch + 新世界生态的突飞猛进,有您的参与会更快些。 +
- 本站由社区建设维护, + 本站由社区建设维护,
- 欢迎来坐坐 + 欢迎来坐坐 - ! +

@@ -31,8 +44,20 @@ function HomepageHeader() { } export default function Home(): JSX.Element { + const homepageTitle = translate({ + id: 'awly.homepage.title', + message: '首页', + description: 'Title of the homepage', + }) + + const homepageDescription = translate({ + id: 'awly.homepage.description', + message: '一站式了解 LoongArch 的上游生态建设', + description: 'Description of the homepage', + }) + return ( - +
diff --git a/src/plugins/awly-asmdb-plugin.ts b/src/plugins/awly-asmdb-plugin.ts index 60802c14..189e4599 100644 --- a/src/plugins/awly-asmdb-plugin.ts +++ b/src/plugins/awly-asmdb-plugin.ts @@ -40,9 +40,12 @@ function runGenerationScript( } export default async function awlyAsmdbPlugin( - _ctx: LoadContext, + ctx: LoadContext, options: PluginOptions, ): Promise> { + const lang = ctx.i18n.currentLocale + const localePrefix = lang == ctx.i18n.defaultLocale ? '' : `/${lang}` + return { name: 'awly-asmdb-plugin', async loadContent() { @@ -57,34 +60,34 @@ export default async function awlyAsmdbPlugin( async contentLoaded({ content, actions }) { const dataPath = await actions.createData('asmdb.json', content) actions.addRoute({ - path: `/asmdb`, + path: `${localePrefix}/asmdb`, component: '@site/src/components/AsmDB', modules: { data: dataPath, }, routes: [ { - path: '/asmdb', + path: `${localePrefix}/asmdb`, component: '@site/src/components/AsmDB', exact: true, }, { - path: '/asmdb/encodingSpaceOverview', + path: `${localePrefix}/asmdb/encodingSpaceOverview`, component: '@site/src/components/AsmDB', exact: true, }, { - path: '/asmdb/insnExplainer', + path: `${localePrefix}/asmdb/insnExplainer`, component: '@site/src/components/AsmDB', exact: true, }, { - path: '/asmdb/insnList', + path: `${localePrefix}/asmdb/insnList`, component: '@site/src/components/AsmDB', exact: true, }, { - path: '/asmdb/vldiHelper', + path: `${localePrefix}/asmdb/vldiHelper`, component: '@site/src/components/AsmDB', exact: true, }, diff --git a/src/plugins/awly-data-plugin.ts b/src/plugins/awly-data-plugin.ts index 748f7280..4ed27af3 100644 --- a/src/plugins/awly-data-plugin.ts +++ b/src/plugins/awly-data-plugin.ts @@ -20,8 +20,18 @@ export type PluginOptions = { sourcePath: string } +// looks like { en: 'xxx', 'zh-Hans': 'xxx', ... } +type I18nString = { [key: string]: string } + +type MaybeTranslated = I18nString | string + +function localize(x: MaybeTranslated, lang: string): string { + if (typeof x === 'string') return x + return x[lang] +} + type CategoryMetadata = { - name: string + name: MaybeTranslated } type InputAuthor = { @@ -160,7 +170,10 @@ async function readProjectDef(p: string): Promise { } } -async function readCategories(sourcePath: string): Promise { +async function readCategories( + sourcePath: string, + lang: string, +): Promise { const categories: { code: string; name: string; projects: IProject[] }[] = [] const srcDir = await opendir(sourcePath) for await (const dirent of srcDir) { @@ -182,7 +195,7 @@ async function readCategories(sourcePath: string): Promise { const projectDefs = await Promise.all(projectDefPaths.map(readProjectDef)) categories.push({ code: dirent.name, - name: categoryMetadata.name, + name: localize(categoryMetadata.name, lang), projects: projectDefs, }) } @@ -196,15 +209,18 @@ async function readCategories(sourcePath: string): Promise { } export default async function awlyDataPlugin( - _ctx: LoadContext, + ctx: LoadContext, options: PluginOptions, ): Promise> { + const lang = ctx.i18n.currentLocale + const localePrefix = lang == ctx.i18n.defaultLocale ? '' : `/${lang}` + return { name: 'awly-data-plugin', async loadContent() { const [authors, categories] = await Promise.all([ readAuthors(path.join(options.sourcePath, authorsFilename)), - readCategories(options.sourcePath), + readCategories(options.sourcePath, lang), ]) return { @@ -225,7 +241,7 @@ export default async function awlyDataPlugin( JSON.stringify(a), ) addRoute({ - path: `/porter/${code}`, + path: `${localePrefix}/porter/${code}`, component: '@site/src/components/AuthorPage', modules: { data: dataPath, @@ -242,7 +258,7 @@ export default async function awlyDataPlugin( JSON.stringify(proj), ) addRoute({ - path: `/project/${proj.code}`, + path: `${localePrefix}/project/${proj.code}`, component: '@site/src/components/ProjectPage', modules: { data: dataPath, diff --git a/src/theme/Footer/Copyright/index.tsx b/src/theme/Footer/Copyright/index.tsx index d7cf527d..0f6f54fe 100644 --- a/src/theme/Footer/Copyright/index.tsx +++ b/src/theme/Footer/Copyright/index.tsx @@ -1,3 +1,4 @@ +import Translate from '@docusaurus/Translate' import Copyright, { type Props } from '@theme-original/Footer/Copyright' import IconExternalLink from '@theme-original/Icon/ExternalLink' @@ -45,12 +46,21 @@ export default function CopyrightWrapper(props: typeof Props): JSX.Element { return ( <>

- 形如此的链接,或部分 + 第一个“形如此”之前的内容(对汉语而言为空) + + 形如此 + + + 第一个与第二个“形如此”之间的内容(对汉语而言为“的链接,或部分”) + - 形如此 + 形如此 - 的链接,其目标主机可能位于中国大陆境外。如需跟进阅读却无法访问,请自行寻找解决方案。 + + 第二个“形如此”之后的内容(对汉语而言为“的链接,其目标主机可能位于中国大陆境外。”) + + 如需跟进阅读却无法访问,请自行寻找解决方案。

    diff --git a/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx b/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx new file mode 100644 index 00000000..db8cbd85 --- /dev/null +++ b/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx @@ -0,0 +1,74 @@ +import React from 'react' +import clsx from 'clsx' +import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +import { useAlternatePageUtils } from '@docusaurus/theme-common/internal' +import { translate } from '@docusaurus/Translate' +import { useLocation } from '@docusaurus/router' +import IconLanguage from '@theme/Icon/Language' +import type { LinkLikeNavbarItemProps } from '@theme/NavbarItem' +import type { Props } from '@theme/NavbarItem/LocaleDropdownNavbarItem' + +import ToggleLikeDropdownNavbarItem from '../ToggleLikeDropdownNavbarItem' +import styles from './styles.module.css' + +export default function LocaleDropdownNavbarItem({ + mobile, + dropdownItemsBefore, + dropdownItemsAfter, + queryString = '', + ...props +}: Props): JSX.Element { + const { + i18n: { currentLocale, locales, localeConfigs }, + } = useDocusaurusContext() + const alternatePageUtils = useAlternatePageUtils() + const { search, hash } = useLocation() + + const localeItems = locales.map((locale): LinkLikeNavbarItemProps => { + const baseTo = `pathname://${alternatePageUtils.createUrl({ + locale, + fullyQualified: false, + })}` + // preserve ?search#hash suffix on locale switches + const to = `${baseTo}${search}${hash}${queryString}` + return { + label: localeConfigs[locale]!.label, + lang: localeConfigs[locale]!.htmlLang, + to, + target: '_self', + autoAddBaseUrl: false, + className: + // eslint-disable-next-line no-nested-ternary + locale === currentLocale + ? // Similar idea as DefaultNavbarItem: select the right Infima active + // class name. This cannot be substituted with isActive, because the + // target URLs contain `pathname://` and therefore are not NavLinks! + mobile + ? 'menu__link--active' + : 'dropdown__link--active' + : '', + } + }) + + const items = [...dropdownItemsBefore, ...localeItems, ...dropdownItemsAfter] + + // Mobile is handled a bit differently + const dropdownLabel = mobile + ? translate({ + message: 'Languages', + id: 'theme.navbar.mobileLanguageDropdown.label', + description: 'The label for the mobile language switcher dropdown', + }) + : localeConfigs[currentLocale]!.label + + return ( + } + label={dropdownLabel} + items={items} + /> + ) +} diff --git a/src/theme/NavbarItem/LocaleDropdownNavbarItem/styles.module.css b/src/theme/NavbarItem/LocaleDropdownNavbarItem/styles.module.css new file mode 100644 index 00000000..fd5cdf52 --- /dev/null +++ b/src/theme/NavbarItem/LocaleDropdownNavbarItem/styles.module.css @@ -0,0 +1,10 @@ +/* swizzle modification: make it look much like a toggle button */ +.iconLanguage { + width: 24px; + height: 24px; +} + +/* swizzle modification: no dropdown arrow for less horizontal space */ +.awlyLocaleDropdown::after { + display: none !important; +} diff --git a/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/index.tsx b/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/index.tsx new file mode 100644 index 00000000..5f31c2d8 --- /dev/null +++ b/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/index.tsx @@ -0,0 +1,206 @@ +import React, { useState, useRef, useEffect } from 'react' +import clsx from 'clsx' +import { + isRegexpStringMatch, + useCollapsible, + Collapsible, +} from '@docusaurus/theme-common' +import { isSamePath, useLocalPathname } from '@docusaurus/theme-common/internal' +import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink' +import NavbarItem, { type LinkLikeNavbarItemProps } from '@theme/NavbarItem' +import type { + DesktopOrMobileNavBarItemProps, + Props as DropdownNavbarItemProps, +} from '@theme/NavbarItem/DropdownNavbarItem' +import styles from './styles.module.css' + +function isItemActive( + item: LinkLikeNavbarItemProps, + localPathname: string, +): boolean { + if (isSamePath(item.to, localPathname)) { + return true + } + if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) { + return true + } + if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) { + return true + } + return false +} + +function containsActiveItems( + items: readonly LinkLikeNavbarItemProps[], + localPathname: string, +): boolean { + return items.some((item) => isItemActive(item, localPathname)) +} + +interface DesktopOrMobileNavBarItemPropsWithIcon + extends DesktopOrMobileNavBarItemProps { + icon?: JSX.Element +} + +interface Props extends DropdownNavbarItemProps { + icon?: JSX.Element +} + +function ToggleLikeDropdownNavbarItemDesktop({ + items, + icon, + position, + className, + onClick, + ...props +}: DesktopOrMobileNavBarItemPropsWithIcon) { + const dropdownRef = useRef(null) + const [showDropdown, setShowDropdown] = useState(false) + + useEffect(() => { + const handleClickOutside = ( + event: MouseEvent | TouchEvent | FocusEvent, + ) => { + if ( + !dropdownRef.current || + dropdownRef.current.contains(event.target as Node) + ) { + return + } + setShowDropdown(false) + } + + document.addEventListener('mousedown', handleClickOutside) + document.addEventListener('touchstart', handleClickOutside) + document.addEventListener('focusin', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + document.removeEventListener('touchstart', handleClickOutside) + document.removeEventListener('focusin', handleClickOutside) + } + }, [dropdownRef]) + + return ( +
    + tag focusable in case no link target + // See https://github.com/facebook/docusaurus/pull/6003 + // There's probably a better solution though... + href={props.to ? undefined : '#'} + className={clsx('navbar__link', className)} + {...props} + label={icon} + onClick={props.to ? undefined : (e) => e.preventDefault()} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault() + setShowDropdown(!showDropdown) + } + }} + > + {props.children ?? props.label} + +
      + {items.map((childItemProps, i) => ( + + ))} +
    +
    + ) +} + +function ToggleLikeDropdownNavbarItemMobile({ + items, + icon, + className, + position, // Need to destructure position from props so that it doesn't get passed on. + onClick, + ...props +}: DesktopOrMobileNavBarItemPropsWithIcon) { + const localPathname = useLocalPathname() + const containsActive = containsActiveItems(items, localPathname) + + const { collapsed, toggleCollapsed, setCollapsed } = useCollapsible({ + initialState: () => !containsActive, + }) + + // Expand/collapse if any item active after a navigation + useEffect(() => { + if (containsActive) { + setCollapsed(!containsActive) + } + }, [localPathname, containsActive, setCollapsed]) + + return ( +
  • + + {icon} + {props.label} + + } + onClick={(e) => { + e.preventDefault() + toggleCollapsed() + }} + > + {props.children ?? props.label} + + + {items.map((childItemProps, i) => ( + + ))} + +
  • + ) +} + +export default function ToggleLikeDropdownNavbarItem({ + mobile = false, + ...props +}: Props): JSX.Element { + const Comp = mobile + ? ToggleLikeDropdownNavbarItemMobile + : ToggleLikeDropdownNavbarItemDesktop + return +} diff --git a/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/styles.module.css b/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/styles.module.css new file mode 100644 index 00000000..10ee92f5 --- /dev/null +++ b/src/theme/NavbarItem/ToggleLikeDropdownNavbarItem/styles.module.css @@ -0,0 +1,3 @@ +.dropdownNavbarItemMobile { + cursor: pointer; +} diff --git a/static/img/logo-en.svg b/static/img/logo-en.svg new file mode 100644 index 00000000..7861780f --- /dev/null +++ b/static/img/logo-en.svg @@ -0,0 +1,60 @@ + + + + + + + + + + AREWELOONGYET? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +