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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ export class PersonaInitialsExample extends React.Component<any, any> {
{ ...examplePersona }
primaryText='Annie Lindqvist'
/>
<Persona
{ ...examplePersona }
primaryText='宋智洋'
/>
<Persona
{ ...examplePersona }
primaryText='남궁 성종'
/>
<Persona
{ ...examplePersona }
primaryText='خسرو رحیمی'
/>
<Persona
{ ...personaWithInitials }
initialsColor={ PersonaInitialsColor.lightBlue }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "Persona: Improve default manner of abbreviating non-Latin names.",
"type": "patch"
}
],
"email": "clewolff@microsoft.com"
}
35 changes: 35 additions & 0 deletions packages/utilities/src/initials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import { getInitials } from './initials';
let { expect } = chai;

describe('getInitials', () => {
it('handles null inputs', () => {
let result = getInitials(null, false);
expect(result).to.equal('');

result = getInitials(undefined, false);
expect(result).to.equal('');
});

it('calculates an expected initials in LTR', () => {
let result = getInitials('Kat Larrson', false);
expect(result).to.equal('KL');
Expand All @@ -27,4 +35,31 @@ describe('getInitials', () => {
let result = getInitials('Kat Larrson', true);
expect(result).to.equal('LK');
});

it('calculates an expected initials for Arabic names', () => {
let result = getInitials('خسرو رحیمی', true);
expect(result).to.equal('ی');
});

it('calculates an expected initials for Chinese names', () => {
let result = getInitials('桂英', false);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very common for Chinese to have 3 characters, sometimes even 4. It would be great to have one such test, just to ensure for instance that no more than 2 characters are returned.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Will add another test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test in f414fd5.

expect(result).to.equal('英');

result = getInitials('佳', false);
expect(result).to.equal('佳');

result = getInitials('宋智洋', false);
expect(result).to.equal('智洋');
});

it('calculates an expected initials for Korean names', () => {
let result = getInitials('강현', false);
expect(result).to.equal('현');

result = getInitials('최종래', false);
expect(result).to.equal('종래');

result = getInitials('남궁 성종', false);
expect(result).to.equal('성종');
});
});
93 changes: 71 additions & 22 deletions packages/utilities/src/initials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,46 @@ const UNICODE_ALPHANUMERIC_CHARS_REGEX =
/** Regex to detect multiple spaces in a string where gi implies global and case-insensitive. */
const MULTIPLE_WHITESPACES_REGEX_TOKEN: RegExp = new RegExp('\\s+', 'gi');

/** Get (up to 2 characters) initials based on display name of the persona. */
export function getInitials(displayName: string, isRtl: boolean): string {
/** Regex to detect Arabic text. */
const ARABIC_LANGUAGE_REGEX = new RegExp('[\u0621-\u064A\u0660-\u0669]');

/** Regex to detect Korean text. */
const KOREAN_LANGUAGE_REGEX = new RegExp('[\u1100-\u11FF|\u3130-\u318F|\uA960-\uA97F|\uAC00-\uD7AF|\uD7B0-\uD7FF]');

/** Regex to detect Chinese text. */
const CHINESE_LANGUAGE_REGEX = new RegExp('[\u4E00-\u9FCC\u3400-\u4DB5\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d]');

function getInitialsArabic(displayName: string, isRtl: boolean): string {
const name = displayName.replace(/\s/, '');

return isRtl ? name[name.length - 1] : name[0];
}

function getInitialsAsian(displayName: string, isRtl: boolean): string {
const name = displayName.replace(/\s/, '');

// For short names, only display a single character of the family name
if (name.length <= 2) {
return isRtl ? name[0] : name[name.length - 1];
}

// For long names, display the two most significant characters of the family name
return isRtl ? name.substr(0, 2) : name.substr(name.length - 2, name.length);
}

function getInitialsLatin(displayName: string, isRtl: boolean): string {
let initials = '';

if (displayName != null) {
// Do not consider the suffixes within parenthesis while computing the initials.
let personaName: string = displayName.replace(CHARS_WITHIN_PARENTHESIS_REGEX, '');
personaName = personaName.replace(UNICODE_ALPHANUMERIC_CHARS_REGEX, '');
personaName = personaName.replace(MULTIPLE_WHITESPACES_REGEX_TOKEN, ' ');

// Trim leading and trailing spaces if any.
personaName = personaName.trim();

const splits: string[] = personaName.split(' ');

if (splits.length === 2) {
initials += splits[0].charAt(0).toUpperCase();
initials += splits[1].charAt(0).toUpperCase();
} else if (splits.length === 3) {
initials += splits[0].charAt(0).toUpperCase();
initials += splits[2].charAt(0).toUpperCase();
} else if (splits.length !== 0) {
initials += splits[0].charAt(0).toUpperCase();
}
const splits: string[] = displayName.split(' ');

if (splits.length === 2) {
initials += splits[0].charAt(0).toUpperCase();
initials += splits[1].charAt(0).toUpperCase();
} else if (splits.length === 3) {
initials += splits[0].charAt(0).toUpperCase();
initials += splits[2].charAt(0).toUpperCase();
} else if (splits.length !== 0) {
initials += splits[0].charAt(0).toUpperCase();
}

if (isRtl && initials.length > 1) {
Expand All @@ -47,3 +63,36 @@ export function getInitials(displayName: string, isRtl: boolean): string {

return initials;
}

function cleanupDisplayName(displayName: string): string {
// Do not consider the suffixes within parenthesis while computing the initials.
displayName = displayName.replace(CHARS_WITHIN_PARENTHESIS_REGEX, '');

// Ignore non-word characters
displayName = displayName.replace(UNICODE_ALPHANUMERIC_CHARS_REGEX, '');

// Make whitespace consistent
displayName = displayName.replace(MULTIPLE_WHITESPACES_REGEX_TOKEN, ' ');
displayName = displayName.trim();

return displayName;
}

/** Get (up to 2 characters) initials based on display name of the persona. */
export function getInitials(displayName: string, isRtl: boolean): string {
if (displayName == null) {
return '';
}

displayName = cleanupDisplayName(displayName);

if (ARABIC_LANGUAGE_REGEX.test(displayName)) {
return getInitialsArabic(displayName, isRtl);
}

if (KOREAN_LANGUAGE_REGEX.test(displayName) || CHINESE_LANGUAGE_REGEX.test(displayName)) {
return getInitialsAsian(displayName, isRtl);
}

return getInitialsLatin(displayName, isRtl);
}