Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/utils/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@ describe('utils/normalize.ts', () => {
expect(normalizeRomanText('Seibuen')).toBe('Seibuen');
});

it('replaces Mine with a spelling English TTS reads as みね', () => {
// 英語 TTS は "Mine" を英単語の「まいん」と読むため、辞書語の綴りへ倒す
expect(normalizeRomanText('Change here for the Mine Line.')).toBe(
'Change here for the Me-nay Line.'
);
expect(normalizeRomanText('The next station is MINE.')).toBe(
'The next station is Me-nay.'
);
expect(normalizeRomanText('The next station is Nishi-Mine.')).toBe(
'The next station is Nishi-me-nay.'
);
// 宇都宮ライトレールの峰(Mine)のような単独の駅名
expect(normalizeRomanText('The next stop is Mine.')).toBe(
'The next stop is Me-nay.'
);
expect(normalizeRomanText('This train is bound for Mine.')).toBe(
'This train is bound for Me-nay.'
);
// 語中・語頭に埋まった mine も置換する
expect(normalizeRomanText('Takamine')).toBe('Taka-me-nay');
expect(normalizeRomanText('Minezaki')).toBe('Me-nay-zaki');
expect(normalizeRomanText('KAMINEYAMA')).toBe('Ka-me-nay-yama');
// mine を含まない語は変えない
expect(normalizeRomanText('Minami-Urawa')).toBe('Minami-urawa');
});

it('keeps Kay-say stable when normalized twice', () => {
// 二重に適用しても結果が変わらないこと(キャッシュキーの安定性)
expect(normalizeRomanText(normalizeRomanText('Keisei Main Line'))).toBe(
Expand All @@ -62,6 +88,12 @@ describe('utils/normalize.ts', () => {
expect(normalizeRomanText(normalizeRomanText('Seibu Shinjuku Line'))).toBe(
'Say-boo Shinjuku Line'
);
expect(normalizeRomanText(normalizeRomanText('Mine Line'))).toBe(
'Me-nay Line'
);
expect(normalizeRomanText(normalizeRomanText('Takamine'))).toBe(
'Taka-me-nay'
);
});

it.each(['Tokyo', 'tOkyo'])('text: %s', (text) => {
Expand Down
13 changes: 13 additions & 0 deletions src/utils/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ const capitalizeSegment = (seg: string): string => {
: seg;
};

// Takamine / Minezaki のように語中・語頭に埋まった "mine" も読み替える。
// 前後に英字が続く場合はハイフンで区切って "Me-nay" を独立させ、
// 語頭のときだけ先頭を大文字にする(Takamine → Taka-me-nay)
const replaceMine = (match: string, offset: number, str: string): string => {
const before = offset > 0 && /[A-Za-z]/.test(str.charAt(offset - 1));
const after = /[A-Za-z]/.test(str.charAt(offset + match.length));
const head = /[A-Z]/.test(match.charAt(0)) ? 'Me' : 'me';
return `${before ? '-' : ''}${head}-nay${after ? '-' : ''}`;
};

// テキストノード(SSML タグの外側)だけに掛ける正規化。タグやその属性値
// (<sub alias="Sta."> や <phoneme ph="..."> 等)を壊さないため、タグ部分には適用しない。
const normalizeTextNode = (text: string): string =>
Expand Down Expand Up @@ -43,6 +53,9 @@ const normalizeTextNode = (text: string): string =>
// ハイフン連結の駅名も語単位で置換する
.replace(/\bKeisei\b/gi, 'Kay-say')
.replace(/\bSeibu\b/gi, 'Say-boo')
// 「mine(美祢・峰など)」は英語 TTS が英単語 "mine"(まいん)として読むため、
// 同じく辞書語 "Me" + "nay" の連結で /mi.neɪ/(みね)に寄せる
.replace(/mine/gi, replaceMine)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// 都営バスを想定
.replace(/\bSta\./gi, ' Station')
.replace(/\bUniv\./gi, ' University')
Expand Down
Loading