LoginSignup
1
2

More than 1 year has passed since last update.

HTMLで和文と欧文の間にスペース(アキ)を自動で挿入する

Posted at

和文と欧文が混じった文章において、読みやすさのため少し文字間のスペース(アキ)を入れたいときがあります。LaTeXなど組版に特化した環境ではそのような機能がサポートされていますが、HTMLでは今のところ標準ではサポートされていません。ただし、半角スペースを直接入力すると

  • 入れるか入れないかの一貫性をとりにくい
  • 幅の調整がしにくい

といった問題があります。そこでJSとCSSで自動的に挿入されるようにします。

以下のコードは中国語向けのこちらを参考にしています。

main.ts
import * as findAndReplaceDOMText  from 'findandreplacedomtext';

enum Space {
  Quarter = 'quarter',
  Half = 'half'
}

class TextAutospace {

  static readonly ELEMENT_NODE = 1;

  quarterPatterns: Array<string>;
  halfPatterns: Array<string>;

  constructor() {
    const hiragana = '\u3040-\u309F~';
    const katakana = '\u30A0-\u30FF';
    const kanji = '\u2E80-\u2FFF\u31C0-\u31EF\u3300-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFE30-\uFE4F';

    const punct: string = '[—@&=_\,\.\?\!\$\%\^\*\-\+\/]';
    const left: string = `[\\[({'"<«‘“]`;
    const right: string = `[\\])}'">»’”]`;

    const cjk = '[' +  [hiragana, katakana, kanji].join('') + ']';
    const latin = '[A-Za-z0-9\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF]' + '|' + punct;

    this.quarterPatterns = [
      '(' + cjk + ')(' + latin + '|' + left + ')',
      '(' + latin + '|' + right + ')(' + cjk + ')',
      '(・)(' + cjk + ')',
      '(' + latin + '|' + cjk + ')([・])',
      '([:])([『])',
      '(' + cjk + ')([:])'
    ];
    this.halfPatterns = [
      '([、。,.)」』])(' + left + '|' + latin + '|' + cjk + ')',
      '(' + latin + '|' + cjk + ')([(「『])'
    ];

  }
  transform(element: Node): void {
    [Space.Quarter, Space.Half].forEach(space => {
      let patterns = (space == Space.Quarter) ?  this.quarterPatterns : this.halfPatterns;
      patterns.forEach(pattern=> {
        findAndReplaceDOMText(element, {
          find: new RegExp(pattern, 'ig'),
          replace: '$1<' + `${space}` + '>$2'
        })
      });

      findAndReplaceDOMText(element, {
        find: `<${space}>`,
        replace: () => {
          let newElement = document.createElement('span');
          newElement.className = space;
          return newElement;
        }
      });

      // Required to keep space before <code></code>
      document.querySelectorAll(`* > span.${space}:first-child`).forEach(child => {
        let p = child.parentNode;
        if (p.firstChild.nodeType == TextAutospace.ELEMENT_NODE) {
          let newElement = document.createElement('span');
          newElement.className = space;
          p.parentNode.insertBefore(newElement, p);
          p.querySelector(`span.${space}:first-child`).remove();
        }
      });

      element.normalize();

    });
  }
}

document.addEventListener('DOMContentLoaded', event => {
  document.body.querySelectorAll('p, li, h1, h2, h3, h4, h5').forEach(element => {
    let t = new TextAutospace();
    t.transform(element);
  });

});

main.scss
$jp-font: 'source-han-sans-cjk-ja';
$western-font: 'Roboto';

body {
  -webkit-font-smoothing: antialiased;
  font-family: $western-font, $jp-font, sans-serif;
  font-feature-settings: 'palt';
  font-weight: 400;
}

.quarter {
  &::after {
    content: ' ';
    display: inline;
    font-family: 'source-han-sans-cjk-ja';
    letter-spacing: -0.1rem;
  }
}

.half {
  &::after {
    content: ' ';
    display: inline;
    font-family: 'source-han-sans-cjk-ja';
  }
}

code {
  .quarter,
  .half {
    display: none;
  }
}

pre {
  .quarter,
  .half {
    display: none;
  }
}

kbd {
  .quarter,
  .half {
    display: none;
  }
}

samp {
  .quarter,
  .half {
    display: none;
  }
}

ol > .quarter,
ul > .quarter,
ul > .half,
ul > .half {
  display: none;
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2