JavaScriptを使用してテキスト内のURLを検出する


151

文字列のセットでURLを検出するための提案はありますか?

arrayOfStrings.forEach(function(string){
  // detect URLs in strings and do something swell,
  // like creating elements with links.
});

更新:この正規表現をリンク検出に使用してしまいました…どうやら数年後のようです。

kLINK_DETECTION_REGEX = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(\s+|$)/gi

フルヘルパー(オプションのハンドルバーサポートあり)は、要旨#1654670です。


11
TLDの新しいセットを作成し続けるため、有限のTLDのセットを一覧表示することはお勧めできません。
Maxy-B 2013

同意します。TLDを使用した更新可能なコードが必要になる場合があります。実際には、TLDを正規表現に追加するビルドスクリプトや、コード内の動的コード更新TLDを使用できます。TLDやタイムゾーンのように、標準化されるべきであることが現実に存在します。実世界のアドレスの使用例で既存の「TLD」検証可能URLを検証するには、有限制御が適切な場合があります。
エドワードチャンJW

回答:


217

まず、URLに一致する適切な正規表現が必要です。これは難しいです。参照してください。ここでここここに

...ほとんどすべてが有効なURLです。それを分割するためのいくつかの句読点の規則があります。句読点がない場合でも、有効なURLがあります。

RFCを注意深く確認し、「無効な」URLを作成できるかどうかを確認してください。ルールは非常に柔軟です。

たとえば:::::、有効なURLです。パスは":::::"です。かなり愚かなファイル名ですが、有効なファイル名です。

また、/////は有効なURLです。netloc( "ホスト名")は""です。パスは"///"です。繰り返しますが、愚かです。また有効です。このURL "///" は、どちらが同等であるかを正規化します。

のようなもの"bad://///worse/////" が完全に有効です。ばかですが有効です。

とにかく、この回答は、最高の正規表現を提供することを意図したものではなく、JavaScriptを使用してテキスト内で文字列を折り返す方法を証明するものです。

わかりましたので、これを使用してみましょう: /(https?:\/\/[^\s]+)/g

繰り返しますが、これは悪い正規表現です。多くの誤検知があります。ただし、この例ではそれで十分です。

function urlify(text) {
  var urlRegex = /(https?:\/\/[^\s]+)/g;
  return text.replace(urlRegex, function(url) {
    return '<a href="' + url + '">' + url + '</a>';
  })
  // or alternatively
  // return text.replace(urlRegex, '<a href="$1">$1</a>')
}

var text = 'Find me at http://www.example.com and also at http://stackoverflow.com';
var html = urlify(text);

console.log(html)

// html now looks like:
// "Find me at <a href="http://www.example.com">http://www.example.com</a> and also at <a href="http://stackoverflow.com">http://stackoverflow.com</a>"

だから要約してみてください:

$$('#pad dl dd').each(function(element) {
    element.innerHTML = urlify(element.innerHTML);
});

4
「多くの誤検知」のいくつかの例は、この回答を大幅に改善します。それ以外の場合、将来のGoogle社員には一部(おそらく有効?)のFUDが残されます。
cmcculloh 2014

私はあなたが2番目のパラメータとして関数を渡すことができることを知りませんでした.replace:|
アーミルアフリディ2015年

4
それは良いことですが、末尾の句読点のtext="Find me at http://www.example.com, and also at http://stackoverflow.com."結果が2つの404になる「間違った」ことを行います。一部のユーザーはこれを認識しており、破損を避けるためにURLの後ろに句読点の前にスペースを追加しますが、私が使用するほとんどのリンク機能(Gmail、イーサパッド、ファブリケータ)は、URLから末尾の句読点を分離しています。
スキーヤーページ2015

テキストに既にアンカーURLが含まれている場合は、関数removeAnchors(text){var div = $( '<div> </ div>')。html(text);を使用できます。div.find( 'a')。contents()。unwrap(); div.text();を返す text.replaceを返す前に最初にアンカーを削除するには
Mirza

テキストに既にアンカーURLが含まれている場合、jqueryを使用してアンカーを削除していますが、私はAngularを使用しています。Angularでアンカーを削除するにはどうすればよいですか?
Sachin Jagtap

132

これが私が私の正規表現として使用したものです:

var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;

これには、URLの末尾の句読点は含まれません。三日月の機能は魅力のように機能します:)だから:

function linkify(text) {
    var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
    return text.replace(urlRegex, function(url) {
        return '<a href="' + url + '">' + url + '</a>';
    });
}

4
最後に、最も明白なケースで実際に機能する正規表現!これはブックマークに値します。私はこれが見つかるまでグーグル検索から何千もの例をテストしました。
イスマエル

6
シンプルでいい!しかし、コンパイルはコストがかかるため、外部urlRegexで定義する必要があります。 linkify
BM

1
これは完全なURLの検出に失敗します:disney.wikia.com/wiki/Pua_(Moana)
Jry9972

1
()キャラクターの各リストに追加しましたが、現在は機能しています。
ギヨーム

3
wwwだけで始まるURLの検出に失敗します。例:www.facebook.com
CraZyDroiD

51

私はしばらくの間この問題をググっていましたが、それを実現するためにいくつかのかなり堅牢な正規表現を利用するAndroidメソッドandroid.text.util.Linkifyがあることに気付きました。幸い、Androidはオープンソースです。

さまざまなタイプのURLを照合するために、いくつかの異なるパターンを使用します。これらはすべてhttp://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.0_r1/android/text/util/Regex.java#Regexで見つけることができます 。 0WEB_URL_PATTERN

WEB_URL_PATTERNに一致するURL、つまりRFC 1738仕様に準拠するURLだけを懸念している場合は、次のように使用できます。

/((?:(http|https|Http|Https|rtsp|Rtsp):\/\/(?:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,64}(?:\:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,25})?\@)?)?((?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]{0,64}\.)+(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnrwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eouw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\:\d{1,5})?)(\/(?:(?:[a-zA-Z0-9\;\/\?\:\@\&\=\#\~\-\.\+\!\*\'\(\)\,\_])|(?:\%[a-fA-F0-9]{2}))*)?(?:\b|$)/gi;

ソースの全文は次のとおりです。

"((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+ "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+ "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
+ "(?:"   // plus top level domain
+ "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+ "|(?:biz|b[abdefghijmnorstvwyz])"
+ "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+ "|d[ejkmoz]"
+ "|(?:edu|e[cegrstu])"
+ "|f[ijkmor]"
+ "|(?:gov|g[abdefghilmnpqrstuwy])"
+ "|h[kmnrtu]"
+ "|(?:info|int|i[delmnoqrst])"
+ "|(?:jobs|j[emop])"
+ "|k[eghimnrwyz]"
+ "|l[abcikrstuvy]"
+ "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
+ "|(?:name|net|n[acefgilopruz])"
+ "|(?:org|om)"
+ "|(?:pro|p[aefghklmnrstwy])"
+ "|qa"
+ "|r[eouw]"
+ "|s[abcdeghijklmnortuvyz]"
+ "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+ "|u[agkmsyz]"
+ "|v[aceginu]"
+ "|w[fs]"
+ "|y[etu]"
+ "|z[amw]))"
+ "|(?:(?:25[0-5]|2[0-4]" // or ip address
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+ "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ "|[1-9][0-9]|[0-9])))"
+ "(?:\\:\\d{1,5})?)" // plus option port number
+ "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+ "(?:\\b|$)";

本当に凝りたいなら、メールアドレスをテストすることもできます。メールアドレスの正規表現は次のとおりです。

/[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}\\@[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+/gi

PS:上記の正規表現でサポートされているトップレベルドメインは2007年6月現在のものです。最新のリストについては、https://data.iana.org/TLD/tlds-alpha-by-domain.txtを確認する必要があります。


3
大文字と小文字を区別しない正規表現があるため、a-zA-Zand を指定する必要はありませんhttp|https|Http|Https|rtsp|Rtsp
Ry-

4
これはいいですが、私はそれを使用するかどうかはわかりません。ほとんどのユースケースでは、TLDのハードコーディングされたリストに依存するアプローチを使用するよりも、いくつかの誤検知を受け入れます。コードにTLDをリストする場合は、いつかは廃止されることを保証しているので、回避できるのであれば、コードに将来の必須のメンテナンスを組み込まない方がよいでしょう。
Mark Amery 2015年

3
これは101%の確率で機能しますが、残念ながら、先頭にスペースがないURLも検出されます。hello@mydomain.comで一致を実行すると、「mydomain.com」をキャッチします。その前にスペースがある場合にのみそれをキャッチするようにこれを改善する方法はありますか?
Deminetix 2015年

また、これはユーザーが入力したURLをキャッチするのに最適です
Deminetix 2015年

grepcode.comは、もはやアップされることに注意してくださいここでは、私が何であると思う Androidのソースコード内の適切な場所へのリンクです。Androidが使用している正規表現は2013年(元の投稿)から更新されている可能性がありますが、2015年以降は更新されていないようで、新しいTLDが不足している可能性があります。
ジェームズ

19

三日月形フレッシュな回答に基づく

http://を含むリンクまたはhttp://を含まないリンクwwwによるリンクを検出する場合以下を使用できます

function urlify(text) {
    var urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
    //var urlRegex = /(https?:\/\/[^\s]+)/g;
    return text.replace(urlRegex, function(url,b,c) {
        var url2 = (c == 'www.') ?  'http://' +url : url;
        return '<a href="' +url2+ '" target="_blank">' + url + '</a>';
    }) 
}

これは良い解決策ですが、テキストにまだhrefが含まれていないことも確認したいと思います。私はこの正規表現を試しました= /((??href)((https?:\/\/)|(www\.)|(mailto:))[^\s]+)/giが機能しません。それで私を助けてくれますか、または上記の正規表現が機能しないのはなぜですか?
Sachin Jagtap

また、返された出力にtarget = "_ blank"を追加したのも気に入っています。このバージョンは私が欲しかったものです。ほとんどのリンクを取得するのに十分なだけの(それ以外の場合はLinkifyjsを使用します)。
Michael Kubler

18

NPMのこのライブラリは、かなり包括的であるように見えますhttps://www.npmjs.com/package/linkifyjs

Linkifyは、プレーンテキストでURLを検索してHTMLリンクに変換するための、小さくても包括的なJavaScriptプラグインです。すべての有効なURLと電子メールアドレスで動作します。


4
私のプロジェクトにlinkifyjsを実装したところ、素晴らしいです。Linkifyjsがこの質問の答えになるはずです。もう1つはgithub.com/twitter/twitter-text
Uber Schnoz

6

画像をレンダリングするために、機能をさらに改善することができます。

function renderHTML(text) { 
    var rawText = strip(text)
    var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;   

    return rawText.replace(urlRegex, function(url) {   

    if ( ( url.indexOf(".jpg") > 0 ) || ( url.indexOf(".png") > 0 ) || ( url.indexOf(".gif") > 0 ) ) {
            return '<img src="' + url + '">' + '<br/>'
        } else {
            return '<a href="' + url + '">' + url + '</a>' + '<br/>'
        }
    }) 
} 

または、フルサイズの画像にリンクするサムネイル画像の場合:

return '<a href="' + url + '"><img style="width: 100px; border: 0px; -moz-border-radius: 5px; border-radius: 5px;" src="' + url + '">' + '</a>' + '<br/>'

そして、これは、既存のhtmlを削除することによってテキスト文字列を均一にするために前処理するstrip()関数です。

function strip(html) 
    {  
        var tmp = document.createElement("DIV"); 
        tmp.innerHTML = html; 
        var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;   
        return tmp.innerText.replace(urlRegex, function(url) {     
        return '\n' + url 
    })
} 

2
let str = 'https://example.com is a great site'
str.replace(/(https?:\/\/[^\s]+)/g,"<a href='$1' target='_blank' >$1</a>")

ショートコード大仕事!...

結果:-

 <a href="https://example.com" target="_blank" > https://example.com </a>

1

既存のnpmパッケージがあります:url-regexyarn add url-regexまたはでインストールしてnpm install url-regex次のように使用してください:

const urlRegex = require('url-regex');

const replaced = 'Find me at http://www.example.com and also at http://stackoverflow.com or at google.com'
  .replace(urlRegex({strict: false}), function(url) {
     return '<a href="' + url + '">' + url + '</a>';
  });

0

tmp.innerTextは未定義です。tmp.innerHTMLを使用する必要があります

function strip(html) 
    {  
        var tmp = document.createElement("DIV"); 
        tmp.innerHTML = html; 
        var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;   
        return tmp.innerHTML .replace(urlRegex, function(url) {     
        return '\n' + url 
    })

0

これを試して:

function isUrl(s) {
    if (!isUrl.rx_url) {
        // taken from https://gist.github.com/dperini/729294
        isUrl.rx_url=/^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i;
        // valid prefixes
        isUrl.prefixes=['http:\/\/', 'https:\/\/', 'ftp:\/\/', 'www.'];
        // taken from https://w3techs.com/technologies/overview/top_level_domain/all
        isUrl.domains=['com','ru','net','org','de','jp','uk','br','pl','in','it','fr','au','info','nl','ir','cn','es','cz','kr','ua','ca','eu','biz','za','gr','co','ro','se','tw','mx','vn','tr','ch','hu','at','be','dk','tv','me','ar','no','us','sk','xyz','fi','id','cl','by','nz','il','ie','pt','kz','io','my','lt','hk','cc','sg','edu','pk','su','bg','th','top','lv','hr','pe','club','rs','ae','az','si','ph','pro','ng','tk','ee','asia','mobi'];
    }

    if (!isUrl.rx_url.test(s)) return false;
    for (let i=0; i<isUrl.prefixes.length; i++) if (s.startsWith(isUrl.prefixes[i])) return true;
    for (let i=0; i<isUrl.domains.length; i++) if (s.endsWith('.'+isUrl.domains[i]) || s.includes('.'+isUrl.domains[i]+'\/') ||s.includes('.'+isUrl.domains[i]+'?')) return true;
    return false;
}

function isEmail(s) {
    if (!isEmail.rx_email) {
        // taken from http://stackoverflow.com/a/16016476/460084
        var sQtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
        var sDtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
        var sAtom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
        var sQuotedPair = '\\x5c[\\x00-\\x7f]';
        var sDomainLiteral = '\\x5b(' + sDtext + '|' + sQuotedPair + ')*\\x5d';
        var sQuotedString = '\\x22(' + sQtext + '|' + sQuotedPair + ')*\\x22';
        var sDomain_ref = sAtom;
        var sSubDomain = '(' + sDomain_ref + '|' + sDomainLiteral + ')';
        var sWord = '(' + sAtom + '|' + sQuotedString + ')';
        var sDomain = sSubDomain + '(\\x2e' + sSubDomain + ')*';
        var sLocalPart = sWord + '(\\x2e' + sWord + ')*';
        var sAddrSpec = sLocalPart + '\\x40' + sDomain; // complete RFC822 email address spec
        var sValidEmail = '^' + sAddrSpec + '$'; // as whole string

        isEmail.rx_email = new RegExp(sValidEmail);
    }

    return isEmail.rx_email.test(s);
}

また、のようなURLを認識して google.comhttp://www.google.blahttp://google.blawww.google.blaではなく、google.bla


0

このような正規表現を使用して、通常のURLパターンを抽出できます。

(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})

より洗練されたパターンが必要な場合は、このようなライブラリを使用してください。

https://www.npmjs.com/package/pattern-dreamer


の目的は(?:www\.|(?!www))何ですか?なぜwwwww.com無効にすべきですか?
トト

あなたが正しいです。実際、私は多くの人が正規表現を使っているのでそれを採用しました。上記のリンクされたライブラリを使用することをお勧めします。URL検出では多くのケースを考慮する必要があるため、正規表現はより複雑になるはずです。
カンアンドリュー

0

一般的なオブジェクト指向ソリューション

DOMを直接操作できないAngularのようなフレームワークを使用する私のような人々のために、文字列を取得してurl/の配列を返す関数を作成しましたplainText必要なUI表現の作成に使用できるオブジェクトの。

URL正規表現

私が使用したURLマッチングには(少し変更) h0mayun正規表現:/(?:(?:https?:\/\/)|(?:www\.))[^\s]+/g

My機能も同様URLの末尾から句読点をドロップ.し、,私が適用されることについて、私は終了合法的なURLよりも、実際の句読点になり、より頻繁に信じている(他の回答がうまく説明としてではなくそれができる!これは厳格な科学ではありません)一致したURLに正規表現を適用します/^(.+?)([.,?!'"]*)$/

Typescriptコード

    export function urlMatcherInText(inputString: string): UrlMatcherResult[] {
        if (! inputString) return [];

        const results: UrlMatcherResult[] = [];

        function addText(text: string) {
            if (! text) return;

            const result = new UrlMatcherResult();
            result.type = 'text';
            result.value = text;
            results.push(result);
        }

        function addUrl(url: string) {
            if (! url) return;

            const result = new UrlMatcherResult();
            result.type = 'url';
            result.value = url;
            results.push(result);
        }

        const findUrlRegex = /(?:(?:https?:\/\/)|(?:www\.))[^\s]+/g;
        const cleanUrlRegex = /^(.+?)([.,?!'"]*)$/;

        let match: RegExpExecArray;
        let indexOfStartOfString = 0;

        do {
            match = findUrlRegex.exec(inputString);

            if (match) {
                const text = inputString.substr(indexOfStartOfString, match.index - indexOfStartOfString);
                addText(text);

                var dirtyUrl = match[0];
                var urlDirtyMatch = cleanUrlRegex.exec(dirtyUrl);
                addUrl(urlDirtyMatch[1]);
                addText(urlDirtyMatch[2]);

                indexOfStartOfString = match.index + dirtyUrl.length;
            }
        }
        while (match);

        const remainingText = inputString.substr(indexOfStartOfString, inputString.length - indexOfStartOfString);
        addText(remainingText);

        return results;
    }

    export class UrlMatcherResult {
        public type: 'url' | 'text'
        public value: string
    }

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.