Skip to content

Commit

Permalink
Migrate: 中身の書き方を変える
Browse files Browse the repository at this point in the history
  • Loading branch information
sevenc-nanashi committed Jan 7, 2025
1 parent db83fed commit f004033
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 115 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"postuninstall": "electron-builder install-app-deps",
"license:generate": "tsx tools/generateLicenses.mts",
"license:merge": "tsx tools/mergeLicenses.mts",
"generate-i18n": "tsx build/generateI18n.ts",
"generate-i18n": "tsx tools/generateI18n.mts",
"storybook": "storybook dev --port 6006",
"storybook:build": "storybook build"
},
Expand Down
10 changes: 5 additions & 5 deletions src/components/Menu/MenuBar/MenuBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ const menudata = computed<MenuItemData[]>(() => [
},
{
type: "root",
label: "表示",
label: t`表示`,
onClick: () => {
closeAllDialog();
},
Expand All @@ -418,29 +418,29 @@ const menudata = computed<MenuItemData[]>(() => [
{ type: "separator" },
{
type: "button",
label: "全画面表示を切り替え",
label: t`全画面表示を切り替え`,
onClick: toggleFullScreen,
disableWhenUiLocked: false,
},
{
type: "button",
label: "拡大",
label: t`拡大`,
onClick: () => {
void zoomIn();
},
disableWhenUiLocked: false,
},
{
type: "button",
label: "縮小",
label: t`縮小`,
onClick: () => {
void zoomOut();
},
disableWhenUiLocked: false,
},
{
type: "button",
label: "拡大率のリセット",
label: t`拡大率のリセット`,
onClick: () => {
void zoomReset();
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sing/ToolBar/ToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
hideBottomSpace
dense
class="sing-time-signature-field"
label="拍子"
:label="t`拍子`"
stackLabel
outlined
>
Expand Down
1 change: 1 addition & 0 deletions src/components/Talk/AudioInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ import { EngineManifest } from "@/openapi";
import { useDefaultPreset } from "@/composables/useDefaultPreset";
import { SLIDER_PARAMETERS } from "@/store/utility";
import { createLogger } from "@/domain/frontend/log";
import { t } from "@/domain/i18n/t";
const props = defineProps<{
activeAudioKey: AudioKey;
Expand Down
19 changes: 9 additions & 10 deletions src/domain/i18n/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
音声を繋げて書き出し: Export Voice Concatenated
テキストを繋げて書き出し: Export Text Concatenated
テキスト読み込み: Import Text
'{portFrom}番ポートが使用中であるため {engineName} は、{portTo}番ポートで起動しました': >-
{engineName} started on port {portTo} because port {portFrom} is already in
use
'{defaultPort}番ポートが使用中であるため {engineName} は、{altPort}番ポートで起動しました': '{engineName} is running on port {altPort} because port {defaultPort} is in use'
話速: Speed
音高: Pitch
抑揚: Intonation
Expand All @@ -21,14 +19,10 @@
アクセント: Accent
イントネーション: Intonation
長さ: Length
音域調整の説明: |
Key Adjustment: Description here
音域調整: Key Adj.
声量調整の説明: |
Power Adjustment: Description here
声量調整: Pow. Adj.
音域: Key Adj.
声量: Vol. Adj.
テンポ: Tempo
拍子: Time Signature
拍子: Time Sig.
スナップ: Snap
トーク: Talk
ソング: Song
Expand All @@ -48,6 +42,11 @@
元に戻す: Undo
やり直す: Redo
すべて選択: すべて選択
表示: View
全画面表示を切り替え: Toggle Full Screen
拡大: Zoom In
縮小: Zoom Out
拡大率のリセット: Reset Zoom
エンジン: Engine
設定: Settings
キー割り当て: Key Bindings
Expand Down
59 changes: 44 additions & 15 deletions src/domain/i18n/t.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,48 @@ const escapeRegExp = (str: string) => {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};

const defaultTemplate = (strings: TemplateStringsArray, values: unknown[]) => {
const defaultTemplate = (
strings: TemplateStringsArray,
values: Record<string, string>[],
) => {
return strings.reduce((acc, str, i) => {
if (i === 0) {
return `${acc}${str}`;
}
return `${acc}${String(values[i - 1])}${str}`;
return `${acc}${String(Object.values(values[i - 1])[0])}${str}`;
}, "");
};

type Language = "ja" | "en";
let language: Language = "ja";
const trsnslationMap: Record<Language, Record<string, string>> = {
ja: {},
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
en: enTranslations,
};
if (typeof location !== "undefined" && typeof location.search === "string") {
const searchParams = new URLSearchParams(location.search);
const lang = searchParams.get("lang");
if (lang === "en") {
language = "en";
}
}

/**
* 翻訳する。
*
* Tagged Functionなので、t`Hello, ${name}`のように使う。
*/
export const t = (strings: TemplateStringsArray, ...values: unknown[]) => {
export const t = (
strings: TemplateStringsArray,
...values: Record<string, string>[]
) => {
return translate({
scope: undefined,
strings,
values,
translations: enTranslations,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
translations: trsnslationMap[language],
});
};

Expand All @@ -34,8 +56,13 @@ export const t = (strings: TemplateStringsArray, ...values: unknown[]) => {
*/
export const st =
(scope: string) =>
(strings: TemplateStringsArray, ...values: unknown[]) =>
translate({ scope, strings, values, translations: enTranslations });
(strings: TemplateStringsArray, ...values: Record<string, string>[]) =>
translate({
scope,
strings,
values,
translations: trsnslationMap[language],
});
/** @private テスト用にエクスポート。*/
export const translate = ({
scope,
Expand All @@ -45,7 +72,7 @@ export const translate = ({
}: {
scope: string | undefined;
strings: TemplateStringsArray;
values: unknown[];
values: Record<string, string>[];
translations?: Record<string, string>;
}) => {
if (!translations) {
Expand All @@ -58,7 +85,9 @@ export const translate = ({
if (i === 0) {
return `${acc}${escapeRegExp(str)}`;
}
return `${acc}\\{(.+?)\\}${escapeRegExp(str)}`;
return `${acc}\\{${escapeRegExp(
Object.keys(values[i - 1])[0],
)}\\}${escapeRegExp(str)}`;
},
scope ? `^${escapeRegExp(scope)}:` : `^`,
) + "$",
Expand All @@ -75,14 +104,14 @@ export const translate = ({
return defaultTemplate(strings, values);
}

const placeholders = key.match(keyPattern)?.slice(1);
if (!placeholders) {
throw new Error("Unexpected error");
let mapped = translation;
if (scope) {
mapped = mapped.replace(`${scope}:`, "");
}
for (const valueWithKey of values) {
const [key, value] = Object.entries(valueWithKey)[0];
mapped = mapped.replace(new RegExp(`{${key}}`, "g"), value);
}

const mapped = placeholders.reduce((acc, placeholder, i) => {
return acc.replaceAll(`{${placeholder}}`, String(values[i]));
}, translation);

return mapped;
};
2 changes: 1 addition & 1 deletion src/store/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const DEFAULT_PROJECT_NAME = "Untitled";
export const formatCharacterStyleName = (
characterName: string,
styleName = DEFAULT_STYLE_NAME,
) => t`${characterName}${styleName})`;
) => t`${{ characterName }}${{ styleName }})`;

export function sanitizeFileName(fileName: string): string {
// \x00 - \x1f: ASCII 制御文字
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 21 additions & 18 deletions tests/unit/domain/i18n.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { translate } from "@/domain/i18n/t";

const createTranslator =
(translations: Record<string, string> | undefined) =>
(strings: TemplateStringsArray, ...values: unknown[]) => {
(strings: TemplateStringsArray, ...values: Record<string, string>[]) => {
return translate({ scope: undefined, strings, values, translations });
};
const createScopedTranslator =
(translations: Record<string, string> | undefined) =>
(scope: string) =>
(strings: TemplateStringsArray, ...values: unknown[]) => {
(strings: TemplateStringsArray, ...values: Record<string, string>[]) => {
return translate({ scope, strings, values, translations });
};

Expand All @@ -21,41 +21,44 @@ test("翻訳できる", () => {
});

test("翻訳が無い時はデフォルトの動作をする", () => {
const piyo = "piyo";
const piyo = "piyoValue";
const t = createTranslator(undefined);
expect(t`ほげふが ${piyo}`).toBe("ほげふが piyo");
expect(t`ほげふが ${{ piyo }}`).toBe("ほげふが piyoValue");
});

test("変数を埋め込める", () => {
const piyo = "piyo";
const piyo = "piyoValue";
const t = createTranslator({
"ほげふが {var}": "hogefuga {var}",
"ほげふが {piyo}": "hogefuga {piyo}",
});
expect(t`ほげふが ${piyo}`).toBe("hogefuga piyo");
expect(t`ほげふが ${{ piyo }}`).toBe("hogefuga piyoValue");
});

test("複数回同じ変数を埋め込める", () => {
const piyo = "piyo";
const piyo = "piyoValue";
const t = createTranslator({
"ほげふが {var}": "hogefuga {var} {var}",
"ほげふが {piyo}": "hogefuga {piyo} {piyo}",
});
expect(t`ほげふが ${piyo}`).toBe("hogefuga piyo piyo");
expect(t`ほげふが ${{ piyo }}`).toBe("hogefuga piyoValue piyoValue");
});

test("変数の順序を変えられる", () => {
const foo = "foo";
const bar = "bar";
const foo = "fooValue";
const bar = "barValue";
const t = createTranslator({
"{1} {0}": "{0} {1}",
"{foo} {bar}": "{bar} {foo}",
});
expect(t`${foo} ${bar}`).toBe("bar foo");
expect(t`${{ foo }} ${{ bar }}`).toBe("barValue fooValue");
});

test("スコープを指定できる", () => {
const foo = "foo";
const bar = "bar";
const foo = "fooValue";
const bar = "barValue";
const st = createScopedTranslator({
"scope:{1} {0}": "{0} {1}",
"{foo} {bar}": "Unreachable",
"other-scope:{foo} {bar}": "Unreachable",
"scope2:{foo} {bar}": "Unreachable",
"scope:{foo} {bar}": "scope:{bar} {foo}",
});
expect(st("scope")`${foo} ${bar}`).toBe("bar foo");
expect(st("scope")`${{ foo }} ${{ bar }}`).toBe("barValue fooValue");
});
Loading

0 comments on commit f004033

Please sign in to comment.