Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -348,18 +348,20 @@ pinyin('赵钱孙李额', { pattern: 'first', toneType: 'none', type: 'array' })
option: 'toneSandhi',
type: 'boolean',
description:
'whether to apply smart tone change to <code>一</code> and <code>不</code>, reference <a href=https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83 target="_blank">维基百科</a>',
'whether to apply smart tone change to <code>一</code> and <code>不</code>, reference <a href=https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83 target="_blank">维基百科</a>, and to two consecutive third-tone syllables (e.g. 你好 → ní hǎo), reference <a href=https://en.wikipedia.org/wiki/Tone_sandhi#Mandarin_Chinese target="_blank">Wikipedia</a>',
default: 'true',
children: [
{
value: 'true',
desc: 'apply',
example: `pinyin('一旦被发现', { toneSandhi: true }); // 'yí dàn bèi fā xiàn'`,
example: `pinyin('一旦被发现', { toneSandhi: true }); // 'yí dàn bèi fā xiàn'
pinyin('你好', { toneSandhi: true }); // 'ní hǎo'`,
},
{
value: 'false',
desc: 'not apply',
example: `pinyin('一旦被发现', { toneSandhi: false }); // 'yī dàn bèi fā xiàn'`,
example: `pinyin('一旦被发现', { toneSandhi: false }); // 'yī dàn bèi fā xiàn'
pinyin('你好', { toneSandhi: false }); // 'nǐ hǎo'`,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,18 +346,20 @@ pinyin('赵钱孙李额', { pattern: 'first', toneType: 'none', type: 'array' })
option: 'toneSandhi',
type: 'boolean',
description:
'是否对<code>一</code>和<code>不</code>应用智能变调,参考<a href=https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83 target="_blank">维基百科</a>',
'是否应用智能变调,包括<code>一</code>和<code>不</code>参考<a href=https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83 target="_blank">维基百科</a>),以及两个连续三声的变调(如 你好 → ní hǎo,参考<a href=https://en.wikipedia.org/wiki/Tone_sandhi#Mandarin_Chinese target="_blank">Wikipedia</a>)',
default: 'true',
children: [
{
value: 'true',
desc: '应用',
example: `pinyin('一旦被发现', { toneSandhi: true }); // 'yí dàn bèi fā xiàn'`,
example: `pinyin('一旦被发现', { toneSandhi: true }); // 'yí dàn bèi fā xiàn'
pinyin('你好', { toneSandhi: true }); // 'ní hǎo'`,
},
{
value: 'false',
desc: '不应用',
example: `pinyin('一旦被发现', { toneSandhi: false }); // 'yī dàn bèi fā xiàn'`,
example: `pinyin('一旦被发现', { toneSandhi: false }); // 'yī dàn bèi fā xiàn'
pinyin('你好', { toneSandhi: false }); // 'nǐ hǎo'`,
},
],
},
Expand Down
4 changes: 2 additions & 2 deletions packages/pinyin-pro/lib/core/pinyin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface BasicOptions extends CommonOptions {
*/
surname?: SurnameMode;
/**
* @description 是否开启「一」和 「不」字的变调。默认开启。参考:https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83
* @description 是否开启变调。默认开启。包含「一」和「不」的变调(参考:https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83),以及两个连续三声的变调(如 你好 → ní hǎo,参考:https://en.wikipedia.org/wiki/Tone_sandhi#Mandarin_Chinese)
* @value true:开启
* @value false:不开启
*/
Expand Down Expand Up @@ -204,7 +204,7 @@ function pinyin(
zhChars,
);

// 一和不变调处理
// 变调处理
list = middlewareToneSandhi(list, options.toneSandhi as boolean);

// nonZh 参数及 removeNonZh 参数
Expand Down
49 changes: 49 additions & 0 deletions packages/pinyin-pro/lib/core/pinyin/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,34 @@ export const middlewareType = (
return list.map((item) => item.result).join(options.separator);
};

const thirdToneToSecondToneMap = {
ǎ: "á",
ǒ: "ó",
ě: "é",
ǐ: "í",
ǔ: "ú",
ǚ: "ǘ",
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
ň: "ń",
"m̌": "ḿ",
"ê̌": "ế",
};

// 轻量三声判断:避免在默认热路径上调用完整的 getNumOfTone
const thirdTonePattern = /ǎ|ǒ|ě|ǐ|ǔ|ǚ|ň|m̌|ê̌/;

const isThirdTone = (item: SingleWordResult) =>
item.isZh && thirdTonePattern.test(item.result);

const convertThirdToneToSecondTone = (pinyin: string) => {
return pinyin.replace(
thirdTonePattern,
(thirdTone) =>
thirdToneToSecondToneMap[
thirdTone as keyof typeof thirdToneToSecondToneMap
],
);
};

// 是否开启变调
export const middlewareToneSandhi = (
list: SingleWordResult[],
Expand All @@ -248,6 +276,27 @@ export const middlewareToneSandhi = (
item.result = item.originPinyin = "bù";
}
});
return list;
}

for (let start = 0; start < list.length; ) {
if (!isThirdTone(list[start])) {
start += 1;
continue;
}

let end = start + 1;
while (end < list.length && isThirdTone(list[end])) {
end += 1;
}

if (end - start === 2) {
const pinyin = convertThirdToneToSecondTone(list[start].result);
list[start].result = pinyin;
list[start].originPinyin = pinyin;
}

start = end;
}
return list;
};
2 changes: 1 addition & 1 deletion packages/pinyin-pro/lib/core/segment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function segment(word: string, options?: SegmentCompleteOptions) {
zhChars,
);

// 一和不变调处理
// 变调处理
list = middlewareToneSandhi(list, options.toneSandhi as boolean);

// nonZh
Expand Down
2 changes: 1 addition & 1 deletion packages/pinyin-pro/test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('basic', () => {

it('[basic]好好', () => {
const result = pinyin('好好学习');
expect(result).to.be.equal('hǎo hǎo xué xí');
expect(result).to.be.equal('háo hǎo xué xí');
});

it('[basic]拼音+非汉字数组', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/pinyin-pro/test/segment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ describe("segment surname mode", () => {
format: OutputFormat.PinyinString
}
);
expect(result).to.be.equal("zēng xiǎo xián nǐhǎo")
expect(result).to.be.equal("zēng xiǎo xián níhǎo")
});
});

Expand Down Expand Up @@ -379,4 +379,4 @@ describe("segment final nonZh", () => {
"xiǎo míng shuòshì bìyè yú zhōngguókēxuéyuàn jìsuànsuǒ , hòu zài rìběnjīngdūdàxué shēnzào 。"
);
});
});
});
4 changes: 2 additions & 2 deletions packages/pinyin-pro/test/special.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ describe("tone sandhi for “不”", () => {

it("[special tone sandhi]要不你走", () => {
const result = pinyin("要不你走");
expect(result).to.be.equal("yào bù zǒu");
expect(result).to.be.equal("yào bù zǒu");
});

// 变调为二声
Expand Down Expand Up @@ -294,7 +294,7 @@ describe('[special tone sandhi]绕口令', () => {

it('[special 々]々', () => {
const result = pinyin('天々向上,好々学习');
expect(result).to.be.equal('tiān tiān xiàng shàng , hǎo hǎo xué xí');
expect(result).to.be.equal('tiān tiān xiàng shàng , háo hǎo xué xí');

const result1 = pinyin('々々');
expect(result1).to.be.equal('tóng tóng');
Expand Down
69 changes: 67 additions & 2 deletions packages/pinyin-pro/test/toneSandhi.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,75 @@
import { pinyin } from '../lib/index';
import { expect, describe, it } from 'vitest';
import { clearCustomDict, customPinyin, pinyin } from '../lib/index';
import { afterEach, expect, describe, it } from 'vitest';

describe("toneSandhi", () => {
afterEach(() => {
clearCustomDict('pinyin');
});

it("[toneSandhi]不", () => {
const result = pinyin("不是", { toneSandhi: false });
expect(result).to.be.equal("bù shì");
});

it("changes the first of two consecutive third tones", () => {
expect(pinyin("你好", { toneSandhi: true })).to.be.equal("ní hǎo");
expect(pinyin("你高", { toneSandhi: true })).to.be.equal("nǐ gāo");
});

it("supports each tone output format", () => {
expect(
pinyin("你好", { toneSandhi: true, toneType: "symbol" }),
).to.be.equal("ní hǎo");
expect(
pinyin("你好", { toneSandhi: true, toneType: "num" }),
).to.be.equal("ni2 hao3");
expect(
pinyin("你好", { toneSandhi: true, toneType: "none" }),
).to.be.equal("ni hao");
});

it("reproduces the issue 286 example", () => {
expect(
pinyin("你好", {
toneSandhi: true,
type: "array",
toneType: "num",
}),
).toEqual(["ni2", "hao3"]);
});

it("keeps both third tones when tone sandhi is disabled", () => {
expect(pinyin("你好", { toneSandhi: false })).to.be.equal("nǐ hǎo");
});

it("does not apply across non-Chinese boundaries", () => {
expect(pinyin("你,好", { toneSandhi: true })).to.be.equal("nǐ , hǎo");
expect(pinyin("你a好", { toneSandhi: true })).to.be.equal("nǐ a hǎo");
});

it("handles separate pairs between punctuation boundaries", () => {
expect(pinyin("你好,很好", { toneSandhi: true })).to.be.equal(
"ní hǎo , hén hǎo",
);
});

it("keeps runs longer than two third tones unchanged", () => {
expect(pinyin("我很好", { toneSandhi: true })).to.be.equal("wǒ hěn hǎo");
expect(pinyin("我想很好", { toneSandhi: true })).to.be.equal(
"wǒ xiǎng hěn hǎo",
);
});

it("uses custom pinyin results for third-tone sandhi", () => {
customPinyin({
你好: "wǒ hǎo",
});

expect(pinyin("你好", { toneSandhi: true })).to.be.equal("wó hǎo");
});

it("preserves existing tone sandhi for 一 and 不", () => {
expect(pinyin("一把", { toneSandhi: true })).to.be.equal("yì bǎ");
expect(pinyin("不想", { toneSandhi: true })).to.be.equal("bù xiǎng");
});
});
2 changes: 1 addition & 1 deletion packages/pinyin-pro/types/core/pinyin/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface BasicOptions extends CommonOptions {
*/
surname?: SurnameMode;
/**
* @description 是否开启「一」和 「不」字的变调。默认开启。参考:https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83
* @description 是否开启变调。默认开启。包含「一」和「不」的变调(参考:https://zh.wiktionary.org/wiki/Appendix:%E2%80%9C%E4%B8%80%E2%80%9D%E5%8F%8A%E2%80%9C%E4%B8%8D%E2%80%9D%E7%9A%84%E5%8F%98%E8%B0%83),以及两个连续三声的变调(如 你好 → ní hǎo,参考:https://en.wikipedia.org/wiki/Tone_sandhi#Mandarin_Chinese)
* @value true:开启
* @value false:不开启
*/
Expand Down
Loading