Skip to content

Commit

Permalink
onboard localization to pnpm/mono-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Pistonight committed Jan 25, 2025
1 parent 1644b7d commit c446212
Show file tree
Hide file tree
Showing 16 changed files with 414,249 additions and 204 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"pnpm": {
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
}
}
}
"pnpm": {
"patchedDependencies": {
"[email protected]": "packages/intwc/patches/[email protected]"
}
}
}
6 changes: 5 additions & 1 deletion packages/intwc/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ tasks:
cmds:
- rm -rf monaco-editor-patch
- pnpm install
- pnpm patch [email protected] --edit-dir monaco-editor-patch
- pnpm patch monaco-editor@$(jq '.dependencies.["monaco-editor"]' package.json | tr -d '"') --edit-dir monaco-editor-patch > out
- rm out
- bun scripts/patch.ts
- rm -r monaco-editor-patch/dev
- rm -r monaco-editor-patch/min
- rm -r monaco-editor-patch/min-maps
- pnpm patch-commit monaco-editor-patch || true
- rm -rf patches
- mv ../../patches .
- bun scripts/post-patch.ts
- pnpm install
- rm -rf monaco-editor-patch
413,752 changes: 413,752 additions & 0 deletions packages/intwc/patches/[email protected]

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions packages/intwc/scripts/post-patch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @ts-ignore
import fs from "node:fs";

const ROOT_PACKAGE = "../../package.json";
const rootPackageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE, "utf-8"));
const packageJson = JSON.parse(fs.readFileSync("package.json", "utf-8"));
const monacoEditorVersion = packageJson.dependencies["monaco-editor"];
const patchedDependencies = rootPackageJson.pnpm.patchedDependencies;

let found = false

for (const key in patchedDependencies) {
if (key === `monaco-editor@${monacoEditorVersion}`) {
found = true;
const file = patchedDependencies[key];
patchedDependencies[key] = "packages/intwc/" + file;
console.log("Patched", key, "to", patchedDependencies[key]);
continue;
}
if (key.startsWith("monaco-editor@")) {
console.log("Removing", key);
delete patchedDependencies[key];
}
}

if (!found) {
throw new Error("monaco-editor not found in patchedDependencies");
}

fs.writeFileSync(ROOT_PACKAGE, JSON.stringify(rootPackageJson, null, 4));
2 changes: 2 additions & 0 deletions packages/localization/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.yaml
*.yml
24 changes: 24 additions & 0 deletions packages/localization/Taskfile.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
version: '3'

includes:
ecma:
taskfile: ../mono-dev/task/ecma.yaml
internal: true

tasks:
push:
desc: Push generated files. Requires gcloud access
cmds:
- gcloud storage cp src/generated/*.yaml gs://ist-private/i18n/generated

build:
desc: Generate the localization files
aliases: [b]
cmds:
- python scripts/generate.py

check:
desc: Run Linter and Formatter
cmds:
- task: ecma:tsc-check
- task: ecma:eslint-check
- task: ecma:prettier-check

fix:
desc: Fix issues in code
cmds:
- task: ecma:eslint-fix
- task: ecma:prettier-fix
6 changes: 6 additions & 0 deletions packages/localization/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { config } from "mono-dev/eslint";

export default config({
ignores: [],
tsconfigRootDir: import.meta.dirname,
});
37 changes: 23 additions & 14 deletions packages/localization/package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
{
"name": "skybook-localization",
"private": true,
"version": "0.0.0",
"type": "module",
"dependencies": {
"@pistonite/pure": "*",
"fuse.js": "^7.0.0",
"i18next": "^24.2.0",
"lru-cache": "^11.0.2",
"react-i18next": "^15.2.0"
},
"exports": {
".": "./src/index.ts"
}
"name": "skybook-localization",
"private": true,
"version": "0.0.0",
"type": "module",
"dependencies": {
"@pistonite/pure": "catalog:",
"fuse.js": "^7.0.0",
"i18next": "^24.2.0",
"lru-cache": "^11.0.2",
"react": "catalog:",
"react-i18next": "^15.2.0"
},
"devDependencies": {
"@modyfi/vite-plugin-yaml": "catalog:",
"@types/react": "catalog:",
"eslint": "catalog:",
"mono-dev": "workspace:*",
"typescript": "catalog:",
"vite": "catalog:"
},
"exports": {
".": "./src/index.ts"
}
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@

import yaml
import os
import multiprocessing
import shutil

LOCALE_MAP = {
"en-US": "USen",
"ja-JP": "JPja",
"de-DE": "EUde",
"es-ES": "EUes",
"it-IT": "EUit",
"fr-FR": "EUfr",
"ru-RU": "EUru",
"zh-CN": "CNzh",
"zh-TW": "TWzh",
"ko-KR": "KRko",
"nl-NL": "EUnl",
}
LOCALIZATION_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
RESEARCH_SCRIPTS_DIR = os.path.join(os.path.dirname(LOCALIZATION_DIR), "research-scripts")
import sys
sys.path.append(os.path.join(RESEARCH_SCRIPTS_DIR, "src"))
import msyt # type: ignore
import spp # type: ignore

def main():
home = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
actor_dir = os.path.join(home, "data", "Actor")

actor_dir = os.path.join(RESEARCH_SCRIPTS_DIR, "output", "Actor")
actor_files = [os.path.join(actor_dir, f) for f in os.listdir(actor_dir) ]
cook_effect_dir = os.path.join(home, "data", "CookEffect")

cook_effect_dir = os.path.join(RESEARCH_SCRIPTS_DIR, "output", "CookEffect")
cook_effect_files = [os.path.join(cook_effect_dir, f) for f in os.listdir(cook_effect_dir) ]
special_status_dir = os.path.join(home, "data", "SpecialStatus")

special_status_dir = os.path.join(RESEARCH_SCRIPTS_DIR, "output", "SpecialStatus")
special_status_files = [os.path.join(special_status_dir, f) for f in os.listdir(special_status_dir) ]

data = {}
for locale in LOCALE_MAP:
for locale in msyt.locale_map:
data[locale] = {}

with multiprocessing.Pool() as pool:
print("loading actor files...")
for result in pool.imap_unordered(load_entries_for_actor, actor_files):
progress = spp.printer(len(actor_files), "Load actor files")
for (i, result) in enumerate(pool.imap_unordered(load_entries_for_actor, actor_files)):
progress.update(i)
if result:
for locale in result:
data[locale].update(result[locale])
print("loading cook effect files...")
for result in pool.imap_unordered(load_entries_for_cook_effect, cook_effect_files):
progress.done()
progress = spp.printer(len(cook_effect_files), "Load cook effect files")
for (i, result) in enumerate(pool.imap_unordered(load_entries_for_cook_effect, cook_effect_files)):
progress.update(i)
if result:
for locale in result:
data[locale].update(result[locale])
print("loading special status files...")
for result in pool.imap_unordered(load_entries_for_special_status, special_status_files):
progress.done()
progress = spp.printer(len(special_status_files), "Load special status files")
for (i, result) in enumerate(pool.imap_unordered(load_entries_for_special_status, special_status_files)):
progress.update(i)
if result:
for locale in result:
data[locale].update(result[locale])
progress.done()

output_dir = os.path.join(os.path.dirname(home), "localization", "src", "generated")
output_dir = os.path.join(LOCALIZATION_DIR, "src", "generated")
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
os.makedirs(output_dir)
Expand All @@ -69,7 +68,7 @@ def load_entries_for_actor(actor_file) -> dict[str, dict[str, str]] | None:
if not l10n:
return None
data = {}
for locale in LOCALE_MAP:
for locale in msyt.locale_map:
# name, replace {effect} with the attributed one
# e.g. feminine, masculine, neuter, plural
name = l10n[locale]["name"]["text"]
Expand Down Expand Up @@ -99,7 +98,7 @@ def load_entries_for_cook_effect(cook_effect_file) -> dict[str, dict[str, str]]
if not l10n:
return None
data = {}
for locale in LOCALE_MAP:
for locale in msyt.locale_map:
name = l10n[locale]["name"]
name_feminine = l10n[locale]["name_feminine"]
name_masculine = l10n[locale]["name_masculine"]
Expand Down Expand Up @@ -129,20 +128,16 @@ def load_entries_for_special_status(special_status_file) -> dict[str, dict[str,
return None
data = {}
name = special_status["name"]
for locale in LOCALE_MAP:
for locale in msyt.locale_map:
value = l10n[locale]
# Some modifiers doesn't show the value in game
# we add it
if name in ["RapidFire", "LongThrow", "SpreadFire", "SurfMaster"]:
value += " [{{modifier_value}}]"
if name == "SurfMaster" and (locale == "zh-CN" or locale == "zh-TW"):
value = "\u76fe\u6ed1\u884c\u63d0\u5347 [{{modifier_value}}]"

data[locale] = {
f"status.{name}": value,
}

# patch for missing entries for Shield Surf Up
return data

if __name__ == "__main__":
Expand Down
80 changes: 47 additions & 33 deletions packages/localization/src/backend.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { BackendModule } from "i18next";
import i18next from "i18next";
import { initReactI18next, useTranslation } from "react-i18next";
import { convertToSupportedLocale, detectLocale, initLocale } from "@pistonite/pure/pref";
import {
convertToSupportedLocale,
detectLocale,
initLocale,
} from "@pistonite/pure/pref";
import { useCallback } from "react";

export const backend: BackendModule = {
Expand All @@ -17,32 +21,36 @@ export const backend: BackendModule = {
const locale = convertToSupportedLocale(language);
let strings;
try {
strings = await import(`./${namespace}/${locale}.yaml`);
} catch(e) {
strings = await import(`./${namespace}/${locale}.yaml`);
} catch (e) {
console.error(e);
try {
strings = await import(`./${namespace}/en-US.yaml`);
} catch(e) {
} catch (e) {
console.error(e);
return undefined;
}
console.warn(`${language} is not supported for ${namespace} namespace. Falling back to en-US.`);
console.warn(
`${language} is not supported for ${namespace} namespace. Falling back to en-US.`,
);
}
return strings.default;
}
}
},
};

export const SupportedLocales =
[
"de-DE",
"en-US",
"es-ES",
"fr-FR",
"it-IT",
"ja-JP", "ko-KR",
"nl-NL",
"ru-RU", "zh-CN", "zh-TW"
] as const;
export const SupportedLocales = [
"de-DE",
"en-US",
"es-ES",
"fr-FR",
"it-IT",
"ja-JP",
"ko-KR",
"nl-NL",
"ru-RU",
"zh-CN",
"zh-TW",
] as const;

export const initI18n = async () => {
initLocale({
Expand All @@ -52,32 +60,38 @@ export const initI18n = async () => {
});

await i18next.use(detectLocale).use(backend).use(initReactI18next).init();
}
};

export const translateUI = (key: string, options?: Record<string, unknown>) => {
return i18next.t(`ui:${key}`, options);
}
export const translateGenerated = (key: string, options?: Record<string, unknown>) => {
};
export const translateGenerated = (
key: string,
options?: Record<string, unknown>,
) => {
const value = i18next.t(`generated:${key}`, options);
if (value === key) {
return "";
}
return value;
}
};

export const useUITranslation = () => {
const {t} = useTranslation("ui");
const { t } = useTranslation("ui");
return t;
}
};

export const useGeneratedTranslation = () => {
const {t} = useTranslation("generated", {nsMode: "default"});
const { t } = useTranslation("generated", { nsMode: "default" });
// return empty string if the key is not found, similar to the game
return useCallback((key: string, options?: Record<string, unknown>) => {
const value = t(key, options);
if (value === key) {
return "";
}
return value;
}, [t]);
}
return useCallback(
(key: string, options?: Record<string, unknown>) => {
const value = t(key, options);
if (value === key) {
return "";
}
return value;
},
[t],
);
};
Loading

0 comments on commit c446212

Please sign in to comment.