Skip to content

Commit

Permalink
i18n: Translate first blog post but keep RSS feed working
Browse files Browse the repository at this point in the history
  • Loading branch information
danielhjacobs committed Nov 16, 2024
1 parent 7e0a012 commit 253b15c
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 57 deletions.
40 changes: 20 additions & 20 deletions blog_posts/2023-03-12-progress-report.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ date: 2023-03-12 01:23:00 +0100
author: nosamu
icon: /undraw/undraw_welcoming_re_x0qo.svg
---
Get ready for the biggest Ruffle announcement yet! And the first one on the blog!
{{blog.20230312.headline}}

---

## Huge improvements to Ruffle's AVM1 engine accuracy!
Thanks to a massive code refactor by [@CUB3D](https://github.com/CUB3D/), dozens upon dozens of ActionScript 2 games have been fixed! Here are just a few of them:
## {{blog.20230312.avm1-improvements}}
{{blog.20230312.avm1-fixes-start}} [@CUB3D](https://github.com/CUB3D/){{blog.20230312.avm1-fixes-end}}
- [Chibi Knight](https://www.newgrounds.com/portal/view/526470)
- [Xeno Tactic 2](https://www.newgrounds.com/portal/view/438241/format/flash?emulate=flash)
- [Trojan War](https://www.newgrounds.com/portal/view/604949/format/flash?emulate=flash)
Expand All @@ -20,44 +20,44 @@ Thanks to a massive code refactor by [@CUB3D](https://github.com/CUB3D/), dozens
- [The Powerpuff Girls: Attack of the Puppybots](https://flasharch.com/en/archive/play/b93354279e9788b849f83ef78f52cbbb)
- [Extreme Pamplona](https://flasharch.com/en/archive/play/e36aac73914ec8672218317e000615d7)

## Incredible progress in AVM2 (ActionScript 3) support!
- Our website now has [a page listing exactly what ActionScript 3 APIs we have implemented](https://ruffle.rs/compatibility/avm2), making it easy to follow our progress. It will be frequently updated!
- XML support is rapidly improving! ActionScript 3 games tend to use a wide variety of XML methods. As Ruffle gains support for these methods, games are springing to life!
- Several problems that caused unresponsive buttons and menus have been fixed. Unclickable buttons in ActionScript 3 games are (mostly) a thing of the past!
- Other major refactors are in progress to improve Ruffle's compatibility with ActionScript 3 frameworks like Haxe.
- [@Aaron1011](https://github.com/Aaron1011/) is working on **Stage3D support**! Thanks to his work, the gorgeous interstellar strategy game Solarmax 2 is now fully playable in Ruffle on the desktop player! Check out the video below.
## {{blog.20230312.avm2-improvements}}
- {{blog.20230312.avm2-progress-start}} [{{blog.20230312.avm2-progress-link}}](https://ruffle.rs/compatibility/avm2){{blog.20230312.avm2-progress-end}}
- {{blog.20230312.xml-support}}
- {{blog.20230312.unresponsive-buttons}}
- {{blog.20230312.framework-compatibility}}
- [@Aaron1011](https://github.com/Aaron1011/) {{blog.20230312.stage3d-support-start}} **{{blog.20230312.stage3d-support}}**{{blog.20230312.stage3d-support-end}}

<video muted autoplay controls>
<source src="/2023-03-12-progress-report/ruffle_solarmax2.mp4" type="video/mp4">
</video>

Many of the fan-favorite **Flipline games are now playable**! Check them out:
{{blog.20230312.flipline-start}} **{{blog.20230312.flipline}}**{{blog.20230312.flipline-end}}
- [Papa's Burgeria](https://www.kongregate.com/games/FliplineStudios/papas-burgeria)
- [Jacksmith](https://www.kongregate.com/games/FliplineStudios/jacksmith)
- [Papa's Cheeseria](https://www.flipline.com/games/papascheeseria/index.html) (requires the Ruffle Chrome extension)
- [Papa's Bakeria](https://www.flipline.com/games/papasbakeria/index.html) (requires the Ruffle Chrome extension)
- [Papa's Cheeseria](https://www.flipline.com/games/papascheeseria/index.html) {{blog.20230312.extension-required}}
- [Papa's Bakeria](https://www.flipline.com/games/papasbakeria/index.html) {{blog.20230312.extension-required}}

<video muted autoplay controls>
<source src="/2023-03-12-progress-report/ruffle_burgeria.mp4" type="video/mp4">
</video>

Here are just a few more of the many ActionScript 3 games that are playable today in Ruffle!
{{blog.20230312.more-as3-games}}
- [Canabalt](https://www.newgrounds.com/portal/view/510303)
- [Bloons Tower Defense 3](https://www.newgrounds.com/portal/view/463445/format/flash?emulate=flash)
- [Diggy](https://www.kongregate.com/games/Vogd/diggy)
- [Wooden Path 2](https://www.kongregate.com/games/Remivision/wooden-path-2)
- [Fracuum](https://www.newgrounds.com/portal/view/594354)
- [Dino Run: Marathon of Doom](https://www.newgrounds.com/portal/view/566176)

## Last but not least, support for **mobile devices** is improving in a big way!
- **Text input boxes** are finally supported on mobile devices! Tapping on a text box within Flash content now brings up the soft keyboard, so you can type into it without using a bluetooth keyboard or other workarounds.
- The **context menu** finally works on iOS! It is activated by a long-press on the screen. To stop this behavior if needed, simply tap the "Hide this menu" option.
## {{blog.20230312.mobile-start}} **{{blog.20230312.mobile}}** {{blog.20230312.mobile-end}}
- **{{blog.20230312.text-input}}** {{blog.20230312.text-input-support}}
- {{blog.20230312.context-menu-start}} **{{blog.20230312.context-menu}}** {{blog.20230312.context-menu-end}}

<video muted autoplay controls>
<source src="/2023-03-12-progress-report/Ruffle_Kongregate_Pizzeria_iPhone.mov" type="video/mp4">
</video>

**We have even more improvements coming very soon!**
- The latest releases now have **dynamic audio buffering**, making audio playback in the most demanding content much smoother! (Thanks to [@szőlő](https://github.com/torokati44/))
- A bug that causes some AVM2 games' intro sounds to repeat over and over will soon be fixed. (Thanks to [@Aaron1011](https://github.com/Aaron1011/))
- [@Dinnerbone](https://github.com/Dinnerbone/) is working on improving drawing accuracy for thin/hairline strokes and scaled objects. [You can follow his progress here](https://github.com/ruffle-rs/ruffle/pull/9981).
**{{blog.20230312.more-soon}}**
- {{blog.20230312.audio-buffering-start}} **{{blog.20230312.audio-buffering}}**{{blog.20230312.audio-buffering-description}}[@szőlő](https://github.com/torokati44/){{blog.20230312.parentheses-end}}
- {{blog.20230312.looping}}[@Aaron1011](https://github.com/Aaron1011/){{blog.20230312.parentheses-end}}
- [@Dinnerbone](https://github.com/Dinnerbone/) {{blog.20230312.hairline-description}} [{{blog.20230312.hairline-link}}](https://github.com/ruffle-rs/ruffle/pull/9981){{blog.20230312.line-end}}
5 changes: 3 additions & 2 deletions src/app/blog/[year]/[month]/[day]/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getPostData, getSortedPostsData, PostPath } from "@/app/blog/utils";
import { Container } from "@mantine/core";
import { BlogPost } from "@/app/blog/post";
import { Metadata } from "next";
import { getBaseTranslation } from "@/app/utils";

export async function generateMetadata(props: {
params: Promise<PostPath>;
Expand All @@ -12,12 +13,12 @@ export async function generateMetadata(props: {
);
const baseUrl = process.env.BASE_URL || "";
return {
title: `${post.title} - Ruffle`,
title: `${getBaseTranslation(post.title)} - Ruffle`,
description: post.excerpt.split("\n")[0].trim(),
metadataBase: baseUrl ? new URL(baseUrl) : null,
openGraph: {
type: "article",
title: post.title,
title: getBaseTranslation(post.title),
siteName: "Ruffle",
publishedTime: post.date.toISOString(),
authors: [post.author],
Expand Down
45 changes: 35 additions & 10 deletions src/app/blog/post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Image from "next/image";
import rehypeRaw from "rehype-raw";
import rehypeSlug from "rehype-slug";
import { useTranslation } from "@/app/translate";
import React from "react";

export interface BlogPostProps {
metadata: PostMetadata;
Expand All @@ -30,6 +31,17 @@ export function BlogPostAndIcon({ metadata, type }: BlogPostProps) {
);
}

function processChild(
child: React.ReactNode,
t: (key: string) => string,
): React.ReactNode {
if (typeof child === "string") {
// Replace all {{key}} occurrences with t("key")
return child.replace(/{{(.*?)}}/g, (_, key) => t(key.trim()));
}
return child; // Return as is if not a string
}

export function BlogPost({ metadata, type }: BlogPostProps) {
const { t } = useTranslation();
const url = `/blog/${metadata.year}/${metadata.month}/${metadata.day}/${metadata.slug}`;
Expand All @@ -55,17 +67,30 @@ export function BlogPost({ metadata, type }: BlogPostProps) {
</Group>
<div>
<Markdown
className={type == "excerpt" ? classes.excerpt : classes.contents}
className={type === "excerpt" ? classes.excerpt : classes.contents}
rehypePlugins={[rehypeRaw, rehypeSlug]}
components={{
a(props) {
return (
<Link href={props.href || "#"} target="_blank">
{props.children}
</Link>
);
},
}}
components={["a", "p", "h2", "li", "strong", "code", "em"].reduce(
(acc, tag) => ({
...acc,
[tag]: (props: { children?: React.ReactNode; href?: string }) =>
tag === "a" ? (
<Link href={props.href || "#"} target="_blank">
{React.Children.map(props.children, (child) =>
processChild(child, t),
)}
</Link>
) : (
React.createElement(
tag,
null,
React.Children.map(props.children, (child) =>
processChild(child, t),
),
)
),
}),
{},
)}
>
{type == "excerpt" ? metadata.excerpt : metadata.content}
</Markdown>
Expand Down
10 changes: 7 additions & 3 deletions src/app/feed.xml/route.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feed } from "feed";
import { getSortedPostsData } from "@/app/blog/utils";
import { getBaseTranslation } from "@/app/utils";

export async function GET() {
const posts = getSortedPostsData();
Expand All @@ -22,8 +23,10 @@ export async function GET() {
feed.addItem({
date: post.date,
link: `https://ruffle.rs/blog/${post.year}/${post.month}/${post.day}/${post.slug}`,
title: post.title,
description: post.excerpt,
title: getBaseTranslation(post.title),
description: post.excerpt.replace(/{{(.*?)}}/g, (_, key) =>
getBaseTranslation(key.trim()),
),
author: [{ name: post.author }],
});
}
Expand All @@ -35,4 +38,5 @@ export async function GET() {
});
}

export const dynamic = "force-static";
//export const dynamic = "force-static";
export const revalidate = 1;
22 changes: 1 addition & 21 deletions src/app/translate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@

import React, { useEffect, useState, useCallback } from "react";
import defaultTranslations from "@/i18n/translations.en.json";
import { getNestedTranslation, TranslationObject } from "@/app/utils";

const languages = {
en: "English",
es: "Español",
// ...
};

type TranslationObject = {
[key: string]: string | TranslationObject;
};

interface LanguageSelectorProps {
className?: string;
}
Expand Down Expand Up @@ -44,23 +41,6 @@ async function getAvailableLanguage() {
return lang;
}

const getNestedTranslation = (
obj: TranslationObject,
key: string,
): string | undefined => {
let acc: TranslationObject | string | undefined = obj;
for (let i = 0; i < key.split(".").length; i++) {
const part = key.split(".")[i];
if (acc && typeof acc !== "string" && acc[part] !== undefined) {
acc = acc[part];
} else {
acc = undefined; // If a part is not found, stop and return undefined
break;
}
}
return typeof acc === "string" ? acc : undefined;
};

async function fetchTranslations(lang: string) {
try {
const translations = await import(`@/i18n/translations.${lang}.json`);
Expand Down
28 changes: 28 additions & 0 deletions src/app/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import defaultTranslations from "@/i18n/translations.en.json";

export type TranslationObject = {
[key: string]: string | TranslationObject;
};

export const getNestedTranslation = (
obj: TranslationObject,
key: string,
): string | undefined => {
let acc: TranslationObject | string | undefined = obj;
for (let i = 0; i < key.split(".").length; i++) {
const part = key.split(".")[i];
if (acc && typeof acc !== "string" && acc[part] !== undefined) {
acc = acc[part];
} else {
acc = undefined; // If a part is not found, stop and return undefined
break;
}
}
return typeof acc === "string" ? acc : undefined;
};

export function getBaseTranslation(translationKey: string): string {
return (
getNestedTranslation(defaultTranslations, translationKey) || translationKey
);
}
38 changes: 37 additions & 1 deletion src/i18n/translations.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,43 @@
"blog": {
"read-more": "Read more...",
"20230312": {
"title": "First post, progress report!"
"title": "First post, progress report!",
"headline": "Get ready for the biggest Ruffle announcement yet! And the first one on the blog!",
"avm1-improvements": "Huge improvements to Ruffle's AVM1 engine accuracy!",
"avm1-fixes-start": "Thanks to a massive code refactor by",
"avm1-fixes-end": ", dozens upon dozens of ActionScript 2 games have been fixed! Here are just a few of them:",
"avm2-improvements": "Incredible progress in AVM2 (ActionScript 3) support!",
"avm2-progress-start": "Our website now has",
"avm2-progress-link": "a page listing exactly what ActionScript 3 APIs we have implemented",
"avm2-progress-end": ", making it easy to follow our progress. It will be frequently updated!",
"xml-support": "XML support is rapidly improving! ActionScript 3 games tend to use a wide variety of XML methods. As Ruffle gains support for these methods, games are springing to life!",
"unresponsive-buttons": "Several problems that caused unresponsive buttons and menus have been fixed. Unclickable buttons in ActionScript 3 games are (mostly) a thing of the past!",
"framework-compatibility": "Other major refactors are in progress to improve Ruffle's compatibility with ActionScript 3 frameworks like Haxe.",
"stage3d-support-start": "is working on",
"stage3d-support": "Stage3D support",
"stage3d-support-end": "! Thanks to his work, the gorgeous interstellar strategy game Solarmax 2 is now fully playable in Ruffle on the desktop player! Check out the video below.",
"flipline-start": "Many of the fan-favorite",
"flipline": "Flipline games are now playable",
"flipline-end": "! Check them out:",
"extension-required": "(requires the Ruffle Chrome extension)",
"more-as3-games": "Here are just a few more of the many ActionScript 3 games that are playable today in Ruffle!",
"mobile-start": "Last but not least, support for",
"mobile": "mobile devices",
"mobile-end": "is improving in a big way!",
"text-input": "Text input boxes",
"text-input-support": "are finally supported on mobile devices! Tapping on a text box within Flash content now brings up the soft keyboard, so you can type into it without using a bluetooth keyboard or other workarounds.",
"context-menu-start": "The",
"context-menu": "context menu",
"context-menu-end": "finally works on iOS! It is activated by a long-press on the screen. To stop this behavior if needed, simply tap the \"Hide this menu\" option.",
"more-soon": "We have even more improvements coming very soon!",
"audio-buffering-start": "The latest releases now have",
"audio-buffering": "dynamic audio buffering",
"audio-buffering-description": ", making audio playback in the most demanding content much smoother! (Thanks to ",
"parentheses-end": ")",
"looping": "A bug that causes some AVM2 games' intro sounds to repeat over and over will soon be fixed. (Thanks to ",
"hairline-description": "is working on improving drawing accuracy for thin/hairline strokes and scaled objects.",
"hairline-link": "You can follow his progress here",
"line-end": "."
},
"20230423": {
"title": "A post-mortem of Ruffle's removal from addons.mozilla.org"
Expand Down

0 comments on commit 253b15c

Please sign in to comment.