├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── SmartFormat.NET-Korean.Tests
├── Extensions
│ └── KoreanFormatterTests.cs
├── Properties
│ └── AssemblyInfo.cs
├── SmartFormat.NET-Korean.Tests.csproj
├── Utilities
│ └── HangulTests.cs
└── packages.config
├── SmartFormat.NET-Korean.sln
├── SmartFormat.NET-Korean
├── Extensions
│ └── KoreanFormatter.cs
├── Properties
│ └── AssemblyInfo.cs
├── SmartFormat.NET-Korean.csproj
├── SmartFormat.NET-Korean.nuspec
├── Utilities
│ └── Hangul.cs
├── nuget.exe
└── packages.config
└── demo_image.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/visualstudio
3 |
4 | ### VisualStudio ###
5 | ## Ignore Visual Studio temporary files, build results, and
6 | ## files generated by popular Visual Studio add-ons.
7 |
8 | # User-specific files
9 | *.suo
10 | *.user
11 | *.userosscache
12 | *.sln.docstates
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | [Ll]og/
28 |
29 | # Visual Studio 2015 cache/options directory
30 | .vs/
31 | # Uncomment if you have tasks that create the project's static files in wwwroot
32 | #wwwroot/
33 |
34 | # MSTest test Results
35 | [Tt]est[Rr]esult*/
36 | [Bb]uild[Ll]og.*
37 |
38 | # NUNIT
39 | *.VisualState.xml
40 | TestResult.xml
41 |
42 | # Build Results of an ATL Project
43 | [Dd]ebugPS/
44 | [Rr]eleasePS/
45 | dlldata.c
46 |
47 | # DNX
48 | project.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | *.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.pfx
196 | *.publishsettings
197 | node_modules/
198 | orleans.codegen.cs
199 |
200 | # Since there are multiple workflows, uncomment next line to ignore bower_components
201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
202 | #bower_components/
203 |
204 | # RIA/Silverlight projects
205 | Generated_Code/
206 |
207 | # Backup & report files from converting an old project file
208 | # to a newer Visual Studio version. Backup files are not needed,
209 | # because we have git ;-)
210 | _UpgradeReport_Files/
211 | Backup*/
212 | UpgradeLog*.XML
213 | UpgradeLog*.htm
214 |
215 | # SQL Server files
216 | *.mdf
217 | *.ldf
218 |
219 | # Business Intelligence projects
220 | *.rdl.data
221 | *.bim.layout
222 | *.bim_*.settings
223 |
224 | # Microsoft Fakes
225 | FakesAssemblies/
226 |
227 | # GhostDoc plugin setting file
228 | *.GhostDoc.xml
229 |
230 | # Node.js Tools for Visual Studio
231 | .ntvs_analysis.dat
232 |
233 | # Visual Studio 6 build log
234 | *.plg
235 |
236 | # Visual Studio 6 workspace options file
237 | *.opt
238 |
239 | # Visual Studio LightSwitch build output
240 | **/*.HTMLClient/GeneratedArtifacts
241 | **/*.DesktopClient/GeneratedArtifacts
242 | **/*.DesktopClient/ModelManifest.xml
243 | **/*.Server/GeneratedArtifacts
244 | **/*.Server/ModelManifest.xml
245 | _Pvt_Extensions
246 |
247 | # Paket dependency manager
248 | .paket/paket.exe
249 | paket-files/
250 |
251 | # FAKE - F# Make
252 | .fake/
253 |
254 | # JetBrains Rider
255 | .idea/
256 | *.sln.iml
257 | *.swp
258 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | solution: SmartFormat.NET-Korean.sln
3 | install:
4 | - sudo apt-get install -y gtk-sharp2
5 | - nuget restore SmartFormat.NET-Korean.sln
6 | - nuget install NUnit.Runners -Version 2.6.4 -OutputDirectory testrunner
7 | script:
8 | - xbuild /p:Configuration=Release SmartFormat.NET-Korean.sln /p:PostBuildEvent=""
9 | - mono ./testrunner/NUnit.Runners.2.6.4/tools/nunit-console.exe ./SmartFormat.NET-Korean.Tests/bin/Release/SmartFormatKorean.Tests.dll
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, What! Studio
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of SmartFormat.NET-Korean nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KoreanFormatter
2 |
3 | [![travis][travis-img]][travis-link]
4 | [![GitHub license][license-img]][license-link]
5 | [![nuget][nuget-img]][nuget-link]
6 |
7 | C#용 [SmartFormat.NET][smartformat.net]에서 사용할 수 있는 한국어 조사 포매터입니다. python용 구현체는 [smartformat-korean][smartformat-korean]을 사용해주세요.
8 |
9 | [smartformat.net]: https://github.com/scottrippey/SmartFormat.NET
10 | [smartformat-korean]: https://github.com/what-studio/smartformat-korean
11 | [travis-img]: https://api.travis-ci.org/what-studio/SmartFormat.NET-Korean.svg?branch=master
12 | [travis-link]: https://travis-ci.org/what-studio/SmartFormat.NET-Korean
13 | [license-img]: https://img.shields.io/badge/license-New%20BSD-blue.svg
14 | [license-link]: https://raw.githubusercontent.com/what-studio/SmartFormat.NET-Korean/master/LICENSE
15 | [nuget-img]: https://img.shields.io/nuget/v/SmartFormat.NET-Korean.svg?label=nuget:%20SmartFormat.Net-Korean
16 |
17 | ## 설치
18 |
19 | Nuget 관리자 콘솔을 이용해서 설치:
20 |
21 | ```cmd
22 | PM> Install-Package SmartFormat.NET-Korean
23 | ```
24 |
25 | 혹은 [Nuget 페이지][nuget-link]에서 다운로드
26 |
27 | [nuget-link]: https://www.nuget.org/packages/SmartFormat.NET-Korean
28 |
29 | ## 사용법
30 |
31 | ```c#
32 | namespace SmartFormatKoreanDemo
33 | {
34 | class Program
35 | {
36 | static void Main(string[] args)
37 | {
38 | Smart.Default.AddExtensions(new KoreanFormatter(Smart.Default));
39 |
40 | Console.WriteLine(Smart.Format("{0:은} {1:을} 먹었다.", "나오", "부엉이"));
41 | Console.WriteLine(Smart.Format("{0:이} 갖고 싶어요.", "나오 피규어"));
42 | Console.WriteLine(Smart.Format("{0:이} {1:을} 춘다.", "티라노", "콩댄스"));
43 | Console.WriteLine(Smart.Format("{0:의} {1:이에요}", "넥슨 왓! 스튜디오", "듀랑고"));
44 | Console.ReadKey();
45 | }
46 | }
47 | }
48 | ```
49 |
50 | 
51 |
52 | ## 자연스러운 조사 선택
53 |
54 | `의`, `도`, `만`, `보다`, `부터`, `까지`, `마저`, `조차`, `에~`,
55 | `께~`, `하~`에는 어떤 단어가 앞서도 형태가 변하지 않습니다:
56 |
57 | > 나오**의**, 모리안**의**, 키홀**의**, 나오**도**, 모리안**도**, 키홀**도**
58 |
59 | 반면 `은(는)`, `이(가)`, `을(를)`, `과(와)`는 앞선 단어의 마지막 음절의 받침
60 | 유무에 따라 형태가 달라집니다:
61 |
62 | > 나오**는**, 모리안**은**, 키홀**은**
63 |
64 | `(으)로~`도 비슷한 규칙을 따르지만 앞선 받침이 `ㄹ`일 경우엔 받침이 없는 것과
65 | 같게 취급합니다:
66 |
67 | > 나오**로**, 모리안**으로**, 키홀**로**
68 |
69 | 서술격 조사 `(이)다`는 어미가 활용되어 다양한 형태로 변형될 수 있습니다:
70 |
71 | > 나오**지만**, 모리안**이지만**, 키홀**이에요**, 나오**예요**
72 |
73 | SmartFormat 한국어 확장은 자동으로 가장 자연스러운 조사 형태를 선택합니다.
74 | 만약 어떤 형태가 자연스러운지 알 수 없을 때에는 `은(는)`, `(으)로`처럼
75 | 모든 형태를 병기합니다.
76 |
77 | ```c#
78 | // "대한민국은 민주공화국이다."
79 | Smart.Format("{0:는} {1:다}.", "대한민국", "민주공화국");
80 |
81 | // "나오는 검은사신으로 불린다.
82 | Smart.Format("{0:은} {1:로} 불린다.", "나오", "검은사신");
83 | ```
84 |
85 | 단어가 숫자로 끝나더라도 자연스러운 조사 형태가 선택됩니다:
86 |
87 | ```c#
88 | // "레벨 10이"
89 | Smart.Format("레벨 {0:이}", 10);
90 | // "레벨 999가"
91 | Smart.Format("레벨 {0:이}", 999);
92 | ```
93 |
94 | 괄호 속 단어나 구두점은 조사 형태를 선택할 때 참고하지 않습니다:
95 |
96 | ```c#
97 | // "모리안,,,이?"
98 | Smart.Format("{0:가}?", "모리안,,,");
99 | // "<듀랑고>를 샀다."
100 | Smart.Format("{0:을} 샀다.", "<듀랑고>");
101 | ```
102 |
103 |
104 | ## 조사 분리
105 |
106 | 문자열에 꾸밈 태그 등을 사용하는 경우에는 조사만 분리해서 표기할 수 있습니다:
107 |
108 | ```c#
109 | // "돌날로"
110 | Smart.Format("{0}{0:-으로}", "돌날");
111 | // "으로"
112 | Smart.Format("{0:-으로}", "마법");
113 | ```
114 |
115 |
116 | ## 만든이와 사용권
117 |
118 | [넥슨][nexon] [왓 스튜디오][what-studio]의 [김찬웅][kexplo]과
119 | [이흥섭][sublee]이 만들었고 [제3조항을 포함하는 BSD 허가서][bsd-3-clause]를
120 | 채택했습니다.
121 |
122 | [nexon]: http://nexon.com/
123 | [what-studio]: https://github.com/what-studio
124 | [sublee]: http://subl.ee/
125 | [kexplo]: http://chanwoong.kim/
126 | [bsd-3-clause]: http://opensource.org/licenses/BSD-3-Clause
127 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.Tests/Extensions/KoreanFormatterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using NUnit.Framework;
4 | using SmartFormat;
5 | using SmartFormat.Extensions;
6 |
7 | namespace KoreanParticleFormatter.Tests.Extensions
8 | {
9 | [TestFixture]
10 | class KoreanFormatterTests
11 | {
12 | [SetUp]
13 | public void Setup()
14 | {
15 | if (!Smart.Default.FormatterExtensions.Any(x => x is SmartFormat.Extensions.KoreanFormatter))
16 | {
17 | Smart.Default.FormatterExtensions.Insert(0, new SmartFormat.Extensions.KoreanFormatter(Smart.Default));
18 | }
19 | }
20 |
21 | [TestCase("{0:ko:아} 안녕", "나오", "나오야 안녕")]
22 | [TestCase("{0:ko:을} 칼로 깎는다", "사과", "사과를 칼로 깎는다")]
23 | [TestCase("{0:ko:으로}", "모리안", "모리안으로")]
24 | [TestCase("{0:ko:으로}", "퍼거스=대장장이", "퍼거스=대장장이로")]
25 | public void Test_simple(string format, object arg0, string expectedResult)
26 | {
27 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
28 | }
29 |
30 | [TestCase("{0:으로}", "퍼거스=대장장이", "퍼거스=대장장이로")]
31 | [TestCase("{0:으로}", "퍼거스(Ferghus)", "퍼거스(Ferghus)로")]
32 | public void Test_Implicit(string format, object arg0, string expectedResult)
33 | {
34 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
35 | }
36 |
37 | [TestCase("{0:를}", "", "을(를)")]
38 | [TestCase("{0:을}", "ㅋㅋㅋ", "ㅋㅋㅋ을(를)")]
39 | [TestCase("{0:은}", "", "은(는)")]
40 | [TestCase("{0:는}", "", "은(는)")]
41 | [TestCase("{0:이}", "", "이(가)")]
42 | [TestCase("{0:가}", "", "이(가)")]
43 | [TestCase("{0:과}", "", "과(와)")]
44 | [TestCase("{0:와}", "", "과(와)")]
45 | [TestCase("{0:으로}", "", "(으)로")]
46 | [TestCase("{0:로}", "", "(으)로")]
47 | public void Test_DoublePostposition(string format, object arg0, string expectedResult)
48 | {
49 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
50 | }
51 |
52 | [TestCase("{0:ko:으로}", "모리안(여신)", "모리안(여신)으로")]
53 | [TestCase("{0:ko:으로}", "퍼거스(대장장이(Ferghus))", "퍼거스(대장장이(Ferghus))로")]
54 | [TestCase("{0:가}?", "모리안,,,", "모리안,,,이?")]
55 | [TestCase("{0:을} 샀다.", "<듀랑고>", "<듀랑고>를 샀다.")]
56 | public void Test_Filter(string format, object arg0, string expectedResult)
57 | {
58 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
59 | }
60 |
61 | [TestCase("{0:ko:은} {1:ko:로} 불린다.", "나오", "검은사신", "나오는 검은사신으로 불린다.")]
62 | public void Test_Arg2(string format, object arg0, object arg1, string expectedResult)
63 | {
64 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0, arg1));
65 | }
66 |
67 | [TestCase("{0}{0:ko:-으로}", "돌날", "돌날로")]
68 | [TestCase("{0:-으로}", "마법", "으로")]
69 | public void Test_DashPrefix(string format, object arg0, string expectedResult)
70 | {
71 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
72 | }
73 |
74 | [TestCase("{0:ko(아):{}} 안녕", "나오", "나오야 안녕")]
75 | [TestCase("{0:ko(아):아}", "나오", "아야")]
76 | public void Test_FormattingOption(string format, object arg0, string expectedResult)
77 | {
78 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
79 | }
80 |
81 | [TestCase("{0:ko(은):{}} {1:ko(으로):{}} 불린다.", "나오", "검은사신", "나오는 검은사신으로 불린다.")]
82 | [TestCase("{0:은} {1:으로} 변신 했다!", "밀레시안", "팔라딘", "밀레시안은 팔라딘으로 변신 했다!")]
83 | public void Test_FormattingOption_Arg2(string format, object arg0, object arg1, string expectedResult)
84 | {
85 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0, arg1));
86 | }
87 |
88 | [TestCase("{0:야}", "친구", "친구야")]
89 | [TestCase("{0:야}", "사랑", "사랑아")]
90 | [TestCase("{0:아}", "사랑", "사랑아")]
91 | [TestCase("{0:여}", "친구", "친구여")]
92 | [TestCase("{0:여}", "사랑", "사랑이여")]
93 | [TestCase("{0:이시여}", "하늘", "하늘이시여")]
94 | [TestCase("{0:이시여}", "바다", "바다시여")]
95 | public void Test_Vocative_Particles(string format, object arg0, string expectedResult)
96 | {
97 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
98 | }
99 |
100 | [TestCase("{0:이다}", "나오", "나오다")]
101 | [TestCase("{0:이다}", "키홀", "키홀이다")]
102 | [TestCase("{0:이에요}", "나오", "나오예요")]
103 | [TestCase("{0:이에요}", "키홀", "키홀이에요")]
104 | [TestCase("{0:입니다}", "나오", "나오입니다")]
105 | [TestCase("{0:입니다}", "키홀", "키홀입니다")]
106 | [TestCase("{0:이다}", "Nao", "Nao(이)다")]
107 | [TestCase("{0:이에요}", "Nao", "Nao(이)에요")]
108 | [TestCase("{0:입니다}", "Nao", "Nao입니다")]
109 | [TestCase("{0:였습니다}", "Nao", "Nao(이)었습니다")]
110 | [TestCase("{0:였습니다}", "키홀", "키홀이었습니다")]
111 | [TestCase("{0:였습니다}", "나오", "나오였습니다")]
112 | [TestCase("{0:이었다}", "나오", "나오였다")]
113 | [TestCase("{0:이었지만}", "나오", "나오였지만")]
114 | [TestCase("{0:이지만}", "나오", "나오지만")]
115 | [TestCase("{0:이지만}", "키홀", "키홀이지만")]
116 | [TestCase("{0:지만}", "나오", "나오지만")]
117 | [TestCase("{0:지만}", "키홀", "키홀이지만")]
118 | [TestCase("{0:다}", "나오", "나오다")]
119 | [TestCase("{0:다}", "키홀", "키홀이다")]
120 | [TestCase("{0:이에요}", "나오", "나오예요")]
121 | [TestCase("{0:이에요}", "키홀", "키홀이에요")]
122 | [TestCase("{0:고}", "나오", "나오고")]
123 | [TestCase("{0:고}", "키홀", "키홀이고")]
124 | [TestCase("{0:고}", "모리안", "모리안이고")]
125 | [TestCase("{0:여서}", "나오", "나오여서")]
126 | [TestCase("{0:여서}", "키홀", "키홀이어서")]
127 | [TestCase("{0:이어서}", "나오", "나오여서")]
128 | [TestCase("{0:이어서}", "키홀", "키홀이어서")]
129 | [TestCase("{0:라고라}?", "키홀", "키홀이라고라?")]
130 | public void Test_Ida(string format, object arg0, string expectedResult)
131 | {
132 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
133 | }
134 |
135 | [TestCase("{0:ko(으로):으로}", "용사", "으로로")]
136 | [TestCase("{0:ko(으로):으로}", "마법", "으로으로")]
137 | [TestCase("{0:ko(으로):{}}", "마법", "마법으로")]
138 | [TestCase("{0:ko(으로):{}}", "나오(Lv.25)", "나오(Lv.25)로")]
139 | [TestCase("{0:ko(으로):{}}", "퍼거(?)스", "퍼거(?)스로")]
140 | [TestCase("{0:ko(으로):{}}", "헬로월드!", "헬로월드!로")]
141 | [TestCase("{0:ko(으로):{}}", "?_?", "?_?(으)로")]
142 | [TestCase("{0:ko(으로):{}}", "마법", "마법으로")]
143 | [TestCase("{0:ko(로서):{}}", "나오", "나오로서")]
144 | [TestCase("{0:ko(로서):{}}", "키홀", "키홀로서")]
145 | [TestCase("{0:ko(로서):{}}", "모리안", "모리안으로서")]
146 | [TestCase("{0:ko(로부터):{}}", "나오", "나오로부터")]
147 | [TestCase("{0:ko(로부터):{}}", "키홀", "키홀로부터")]
148 | [TestCase("{0:ko(로부터):{}}", "모리안", "모리안으로부터")]
149 | public void Test_Euro(string format, object arg0, string expectedResult)
150 | {
151 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
152 | }
153 |
154 |
155 | [TestCase("{0:도}", "나오", "나오도")]
156 | [TestCase("{0:도}", "모리안", "모리안도")]
157 | [TestCase("{0:에서}", "판교", "판교에서")]
158 | [TestCase("{0:에서는}", "판교", "판교에서는")]
159 | [TestCase("{0:께서도}", "선생님", "선생님께서도")]
160 | [TestCase("{0:의}", "나오", "나오의")]
161 | [TestCase("{0:만}", "모리안", "모리안만")]
162 | [TestCase("{0:하고}", "키홀", "키홀하고")]
163 | public void Test_InvariantParticles(string format, object arg0, string expectedResult)
164 | {
165 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
166 | }
167 |
168 | [TestCase("금화 {0:이}", "10", "금화 10이")]
169 | [TestCase("레벨 {0:이}", "999", "레벨 999가")]
170 | [TestCase("금화 {0:이}", 10, "금화 10이")]
171 | [TestCase("레벨 {0:이}", 999, "레벨 999가")]
172 | public void Test_Number(string format, object arg0, string expectedResult)
173 | {
174 | Assert.AreEqual(expectedResult, Smart.Format(format, arg0));
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
6 | // 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
7 | // 이러한 특성 값을 변경하세요.
8 | [assembly: AssemblyTitle("KoreanFormatter.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("KoreanFormatter.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
18 | // 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
19 | // 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
20 | [assembly: ComVisible(false)]
21 |
22 | // 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
23 | [assembly: Guid("8ef1a879-19e4-41bb-88c4-ceada2e7d190")]
24 |
25 | // 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
26 | //
27 | // 주 버전
28 | // 부 버전
29 | // 빌드 번호
30 | // 수정 버전
31 | //
32 | // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
33 | // 지정되도록 할 수 있습니다.
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.Tests/SmartFormat.NET-Korean.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8EF1A879-19E4-41BB-88C4-CEADA2E7D190}
8 | Library
9 | Properties
10 | SmartFormatKorean.Tests
11 | SmartFormatKorean.Tests
12 | v4.0
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
36 | True
37 |
38 |
39 | ..\packages\SmartFormat.NET.1.6.1.0\lib\net40\SmartFormat.dll
40 | True
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}
63 | SmartFormat.NET-Korean
64 |
65 |
66 |
67 |
74 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.Tests/Utilities/HangulTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NUnit.Framework;
3 | using SmartFormat.Utilities;
4 |
5 | namespace KoreanParticleFormatter.Tests.Utilities
6 | {
7 | [TestFixture]
8 | internal class HangulTests
9 | {
10 | private readonly Hangul _hangul = new Hangul();
11 |
12 | [TestCase('ㅇ', 'ㅏ', 'ㄴ', '안')]
13 | [TestCase('ㄴ', 'ㅕ', 'ㅇ', '녕')]
14 | [TestCase('ㅎ', 'ㅏ', '\0', '하')]
15 | [TestCase('ㅅ', 'ㅔ', '\0', '세')]
16 | [TestCase('ㅇ', 'ㅛ', '\0', '요')]
17 | public void Test_JoinPhonemes(char onset, char nuclues, char coda, char expectedResult)
18 | {
19 | Assert.AreEqual(expectedResult, _hangul.JoinPhonemes(onset, nuclues, coda));
20 | }
21 |
22 | [TestCase('안', new char[] { 'ㅇ', 'ㅏ', 'ㄴ' })]
23 | [TestCase('녕', new char[] { 'ㄴ', 'ㅕ', 'ㅇ' })]
24 | [TestCase('하', new char[] { 'ㅎ', 'ㅏ', '\0'})]
25 | [TestCase('세', new char[] { 'ㅅ', 'ㅔ', '\0'})]
26 | [TestCase('요', new char[] { 'ㅇ', 'ㅛ', '\0'})]
27 | public void Test_SplitPhonemes(char letter, char[] expectedResult)
28 | {
29 | Assert.AreEqual(expectedResult, _hangul.SplitPhonemes(letter));
30 | }
31 |
32 | [TestCase("10", '십')]
33 | [TestCase("200", '백')]
34 | [TestCase("3000", '천')]
35 | [TestCase("40000", '만')]
36 | [TestCase("500000", '만')]
37 | [TestCase("6000000", '만')]
38 | [TestCase("70000000", '만')]
39 | [TestCase("800000000", '억')]
40 | [TestCase("9000000000", '억')]
41 | [TestCase("1000001000", '천')]
42 | [TestCase("2000003020", '십')]
43 | [TestCase("0", '영')]
44 | [TestCase("1", '일')]
45 | [TestCase("2", '이')]
46 | [TestCase("3", '삼')]
47 | [TestCase("4", '사')]
48 | [TestCase("5", '오')]
49 | [TestCase("6", '육')]
50 | [TestCase("7", '칠')]
51 | [TestCase("8", '팔')]
52 | [TestCase("9", '구')]
53 | public void Test_LastHangulCharacterFromNumber(string numberString, char expectedResult)
54 | {
55 | Assert.AreEqual(expectedResult, _hangul.PickLastHangulCharacterFromNumber(numberString));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25123.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartFormat.NET-Korean", "SmartFormat.NET-Korean\SmartFormat.NET-Korean.csproj", "{F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartFormat.NET-Korean.Tests", "SmartFormat.NET-Korean.Tests\SmartFormat.NET-Korean.Tests.csproj", "{8EF1A879-19E4-41BB-88C4-CEADA2E7D190}"
9 | ProjectSection(ProjectDependencies) = postProject
10 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5} = {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {8EF1A879-19E4-41BB-88C4-CEADA2E7D190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {8EF1A879-19E4-41BB-88C4-CEADA2E7D190}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {8EF1A879-19E4-41BB-88C4-CEADA2E7D190}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {8EF1A879-19E4-41BB-88C4-CEADA2E7D190}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | GlobalSection(SolutionProperties) = preSolution
29 | HideSolutionNode = FALSE
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/Extensions/KoreanFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 | using System.Text.RegularExpressions;
5 | using SmartFormat.Core.Extensions;
6 | using SmartFormat.Utilities;
7 |
8 | namespace SmartFormat.Extensions
9 | {
10 | public class KoreanFormatter : IFormatter
11 | {
12 | private string[] names = { "ko", "" };
13 | public string[] Names { get { return names; } set { names = value; } }
14 | private readonly SmartFormatter _formatter = null;
15 | private Hangul _hangul = new Hangul();
16 |
17 | private readonly Regex _filterPattern = new Regex(@"\(.*[^\(]?\)|[!@#$%^$*?,.:;'""\[\]{}<>]+");
18 |
19 | internal class SyllableInfo
20 | {
21 | ///
22 | /// Evaluate the syllable information of Hangul character.
23 | ///
24 | /// `Hangul` unicode range: 가(U+AC00) ~ 힣(U+D7A3)
25 | ///
26 | /// A `Hangul` character.
27 | public SyllableInfo(char hangulChar)
28 | {
29 | int jongsungExpr = (hangulChar - '가') % 28;
30 | HasCoda = jongsungExpr != 0;
31 | HasRieulCoda = jongsungExpr == 8;
32 | System.Collections.Generic.Dictionary a;
33 | }
34 |
35 | // in Korean, `Coda` means final position of syllable
36 | public bool HasCoda;
37 | public bool HasRieulCoda;
38 | }
39 |
40 | // Particle phonology
41 | private readonly string[] _simpleParticles =
42 | {
43 | "을를", "아야", "이가", "은는", "과와"
44 | };
45 |
46 | private readonly string[] _idaExcepts =
47 | {
48 | "여", "시여"
49 | };
50 |
51 | private readonly Regex _invariantParticlePattern = new Regex(@"^((의|도|만|보다|부터|까지|마저|조차)$|에|께|하)");
52 |
53 | private readonly Regex _euroPattern = new Regex(@"^(으|\(으\))?로");
54 | private readonly Regex _idaPrefixPattern = new Regex(@"^이|\(이\)");
55 |
56 | public KoreanFormatter(SmartFormatter formatter)
57 | {
58 | _formatter = formatter;
59 | }
60 |
61 | public bool TryEvaluateFormat(IFormattingInfo formattingInfo)
62 | {
63 | if (formattingInfo.Format == null || string.IsNullOrEmpty(formattingInfo.Format.RawText))
64 | {
65 | return false;
66 | }
67 |
68 | string currentValue = null;
69 | if (formattingInfo.CurrentValue is string)
70 | {
71 | currentValue = (string) formattingInfo.CurrentValue;
72 | }
73 | else
74 | {
75 | currentValue = formattingInfo.CurrentValue.ToString();
76 | }
77 |
78 | SyllableInfo syllableInfo = EvaluateSyllable(currentValue);
79 |
80 | var format = formattingInfo.FormatterOptions;
81 | bool implicitly = string.IsNullOrEmpty(format);
82 | bool onlyParticle = false;
83 | if (implicitly)
84 | {
85 | format = formattingInfo.Format.RawText;
86 | onlyParticle = format[0] == '-';
87 | if (onlyParticle)
88 | {
89 | format = format.Substring(1);
90 | }
91 | }
92 |
93 | string particle = ParticleConverter(format, syllableInfo);
94 | if (string.IsNullOrEmpty(particle))
95 | {
96 | return false;
97 | }
98 |
99 | if (onlyParticle)
100 | {
101 | formattingInfo.Write(particle);
102 | }
103 | else
104 | {
105 | if (implicitly)
106 | {
107 | formattingInfo.Write(currentValue);
108 | formattingInfo.Write(particle);
109 | }
110 | else
111 | {
112 | var newFormat = this._formatter.Parser.ParseFormat(formattingInfo.Format.RawText + particle);
113 | formattingInfo.Write(newFormat, formattingInfo.CurrentValue);
114 | }
115 | }
116 | return true;
117 | }
118 |
119 | private bool TryParseIda(string format, SyllableInfo syllableInfo, out string result)
120 | {
121 | // remove "이" or "(이)" prefix
122 | var suffix = _idaPrefixPattern.Replace(format, "");
123 | if (string.IsNullOrEmpty(suffix))
124 | {
125 | result = null;
126 | return false;
127 | }
128 |
129 | if (!_idaExcepts.Contains(suffix))
130 | {
131 | var phonemes = _hangul.SplitPhonemes(suffix[0]);
132 | if (phonemes == null)
133 | {
134 | result = null;
135 | return false;
136 | }
137 | var onset = phonemes[0];
138 | var nucleus = phonemes[1];
139 | var coda = phonemes[2];
140 | if (onset == 'ㅇ')
141 | {
142 | if (nucleus == 'ㅣ')
143 | {
144 | // No allomorphs when a form starts with "이" and has a coda.
145 | result = suffix;
146 | return true;
147 | }
148 |
149 | bool hasCoda = (syllableInfo == null || syllableInfo.HasCoda);
150 | char nextNucleus = '\0';
151 | if (!hasCoda && (nucleus == 'ㅓ' || nucleus == 'ㅔ'))
152 | {
153 | nextNucleus = (nucleus == 'ㅓ') ? 'ㅕ' : 'ㅖ';
154 | }
155 | else if (hasCoda && (nucleus == 'ㅕ' || nucleus == 'ㅖ'))
156 | {
157 | nextNucleus = (nucleus == 'ㅕ') ? 'ㅓ' : 'ㅔ';
158 | }
159 |
160 | if (nextNucleus != '\0')
161 | {
162 | var nextLetter = _hangul.JoinPhonemes('ㅇ', nextNucleus, coda);
163 | suffix = nextLetter + suffix.Substring(1);
164 | }
165 | }
166 | }
167 |
168 | if (syllableInfo == null)
169 | {
170 | result = "(이)" + suffix;
171 | }
172 | else
173 | {
174 | result = syllableInfo.HasCoda ? '이' + suffix : suffix;
175 | }
176 | return true;
177 | }
178 |
179 | private SyllableInfo EvaluateSyllable(string value)
180 | {
181 | var filteredValue = _filterPattern.Replace(value, "");
182 |
183 | if (string.IsNullOrEmpty(filteredValue))
184 | {
185 | return null;
186 | }
187 |
188 | var lastChar = filteredValue[filteredValue.Length - 1];
189 |
190 | if (Hangul.IsNumericChar(lastChar))
191 | {
192 | lastChar = _hangul.PickLastHangulCharacterFromNumber(value);
193 | }
194 |
195 | // `Hangul` unicode range: 가(U+AC00) ~ 힣(U+D7A3)
196 | if (!(('가' <= lastChar) && (lastChar <= '힣')))
197 | {
198 | return null;
199 | }
200 |
201 | return new SyllableInfo(lastChar);
202 | }
203 |
204 | private string ParticleConverter(string josaFormat, SyllableInfo syllableInfo)
205 | {
206 | if (josaFormat.Length == 1)
207 | {
208 | var josa = josaFormat[0];
209 | foreach (var j in _simpleParticles)
210 | {
211 | if (josa == j[0] || josa == j[1])
212 | {
213 | if (syllableInfo == null)
214 | {
215 | return string.Format("{0}({1})", j[0], j[1]);
216 | }
217 |
218 | int toIndex = syllableInfo.HasCoda ? 0 : 1;
219 | return j[toIndex].ToString();
220 | }
221 | }
222 | }
223 |
224 | var euroMatch = _euroPattern.Match(josaFormat);
225 | if (euroMatch.Success)
226 | {
227 | // remove '으로' prefix
228 | var suffixString = josaFormat.Substring(euroMatch.Value.Length);
229 | if (syllableInfo == null)
230 | {
231 | return string.Format("(으)로{0}", suffixString);
232 | }
233 |
234 | return (!syllableInfo.HasCoda || syllableInfo.HasRieulCoda) ? '로' + suffixString: "으로" + suffixString;
235 | }
236 |
237 | if (_invariantParticlePattern.IsMatch(josaFormat))
238 | {
239 | return josaFormat;
240 | }
241 |
242 | string idaResult = null;
243 | if (TryParseIda(josaFormat, syllableInfo, out idaResult))
244 | {
245 | return idaResult;
246 | }
247 |
248 | return null;
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
6 | // 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
7 | // 이러한 특성 값을 변경하세요.
8 | [assembly: AssemblyTitle("KoreanFormatter")]
9 | [assembly: AssemblyDescription("Korean Formatter for SmartFormat.Net")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("What! Studio in Nexon Korea")]
12 | [assembly: AssemblyProduct("")]
13 | [assembly: AssemblyCopyright("Copyright 2016 Nexon Korea")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
18 | // 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
19 | // 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
20 | [assembly: ComVisible(false)]
21 |
22 | // 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
23 | [assembly: Guid("f00ea807-2975-41f6-ad8f-c1bce7a3b4f5")]
24 |
25 | // 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
26 | //
27 | // 주 버전
28 | // 부 버전
29 | // 빌드 번호
30 | // 수정 버전
31 | //
32 | // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
33 | // 지정되도록 할 수 있습니다.
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.2")]
36 | [assembly: AssemblyFileVersion("1.0.2")]
37 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/SmartFormat.NET-Korean.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F00EA807-2975-41F6-AD8F-C1BCE7A3B4F5}
8 | Library
9 | Properties
10 | SmartFormatKorean
11 | SmartFormatKorean
12 | v4.0
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | ..\packages\SmartFormat.NET.1.6.1.0\lib\net40\SmartFormat.dll
36 | True
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | $(ProjectDir)nuget pack $(ProjectPath) -Prop Configuration=Release -IncludeReferencedProjects
57 |
58 |
65 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/SmartFormat.NET-Korean.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SmartFormat.NET-Korean
5 | $version$
6 | SmartFormat.NET-Korean
7 | $author$
8 | $author$
9 | https://github.com/what-studio/SmartFormat.Net-Korean/blob/master/LICENSE
10 | https://github.com/what-studio/SmartFormat.Net-Korean
11 | false
12 | $description$
13 | Copyright 2016 Nexon
14 |
15 |
16 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/Utilities/Hangul.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Security.Policy;
6 | using System.Text;
7 |
8 | namespace SmartFormat.Utilities
9 | {
10 | ///
11 | /// Manipulates Hangul letters.
12 | ///
13 | public class Hangul
14 | {
15 | private readonly char[] _onsets =
16 | {
17 | 'ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ','ㅆ','ㅇ','ㅈ',
18 | 'ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
19 | };
20 |
21 | private readonly char[] _nucleuses =
22 | {
23 | 'ㅏ','ㅐ','ㅑ','ㅒ','ㅓ','ㅔ','ㅕ','ㅖ','ㅗ','ㅘ','ㅙ','ㅚ','ㅛ',
24 | 'ㅜ','ㅝ','ㅞ','ㅟ','ㅠ','ㅡ','ㅢ','ㅣ'
25 | };
26 |
27 | private readonly char[] _codas =
28 | {
29 | '\0', 'ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄼ','ㄽ','ㄾ',
30 | 'ㄿ','ㅀ','ㅁ','ㅂ','ㅄ','ㅅ','ㅆ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
31 | };
32 |
33 | private readonly string _hangulDigits = "영일이삼사오육칠팔구";
34 | private readonly Dictionary _hangul10Digits = new Dictionary
35 | {
36 | {1, '십'}, {2, '백'}, {3, '천'}, {4, '만'},
37 | {8, '억'}, {12, '조'}, {16, '경'}, {20, '해'},
38 | {24, '자'}, {28, '양'}, {32, '구'}, {36, '간'},
39 | // 52: 항하사
40 | {40, '정'}, {44, '재'}, {48, '극'}, {52, '사'},
41 | // 56: 아승기, 60: 나유타, 64: 불가사의, 68: 무량대수
42 | {56, '기'}, {60, '타'}, {64, '의'}, {68, '수'},
43 | {72, '겁'}, {76, '업'}
44 | };
45 |
46 |
47 | public char JoinPhonemes(char onset, char nucleus, char coda='\0')
48 | {
49 | return (char)((Array.IndexOf(_onsets, onset) * _nucleuses.Length + Array.IndexOf(_nucleuses, nucleus)) * _codas.Length + Array.IndexOf(_codas, coda) + '가');
50 | }
51 |
52 | public char[] SplitPhonemes(char letter, bool onset = true, bool nucleus = true, bool coda = true)
53 | {
54 | if (!(('가' <= letter) && (letter <= '힣')))
55 | {
56 | return null;
57 | }
58 |
59 | var phonemes = new char[3];
60 |
61 | var offset = letter - '가';
62 | if (onset)
63 | {
64 | phonemes[0] = _onsets[offset / (_nucleuses.Length * _codas.Length)];
65 | }
66 | if (nucleus)
67 | {
68 | phonemes[1] = _nucleuses[(offset / _codas.Length) % _nucleuses.Length];
69 | }
70 | if (coda)
71 | {
72 | phonemes[2] = _codas[offset % _codas.Length];
73 | }
74 | return phonemes;
75 | }
76 |
77 | public static bool IsNumericChar(char c)
78 | {
79 | return (('0' <= c) && (c <= '9'));
80 | }
81 |
82 | public char PickLastHangulCharacterFromNumber(string value)
83 | {
84 | // finds the last non-zero digit.
85 | int startIndex = value.Length;
86 | for (int i = value.Length-1; i >= 0; i--)
87 | {
88 | if (!IsNumericChar(value[i]))
89 | {
90 | break;
91 | }
92 | startIndex = i;
93 | if (value[i] != '0')
94 | {
95 | break;
96 | }
97 | }
98 |
99 | int numberLength = value.Length - startIndex;
100 | if (numberLength == 1)
101 | {
102 | int toInt = (value[startIndex] - '0');
103 | return _hangulDigits[toInt];
104 | }
105 |
106 | int findKey = -1;
107 | foreach (var length in _hangul10Digits.Keys)
108 | {
109 | if (length == numberLength - 1)
110 | {
111 | return _hangul10Digits[length];
112 | }
113 | if (length > numberLength - 1)
114 | {
115 | break;
116 | }
117 | findKey = length;
118 | }
119 |
120 | // can't found key
121 | if (findKey == -1)
122 | {
123 | return _hangul10Digits.Last().Value;
124 | }
125 |
126 | return _hangul10Digits[findKey];
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/what-studio/SmartFormat.NET-Korean/cd149be3a683a2ef49fbef44e31308d39c3ca375/SmartFormat.NET-Korean/nuget.exe
--------------------------------------------------------------------------------
/SmartFormat.NET-Korean/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/demo_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/what-studio/SmartFormat.NET-Korean/cd149be3a683a2ef49fbef44e31308d39c3ca375/demo_image.png
--------------------------------------------------------------------------------